bread 块设备读取函数解析(1)
引言
bread 塊設備讀取函數,顧名思義就是讀取塊設備內容的函數,這個函數的第一次調用是在main函數里面進程1的init()函數里面,sys_setup函數調用的。
第一次調用主要是為了讀取硬盤上的第一個扇區的內容,因為第一個扇區有著分區表等重要信息。
bread的技術路線特別長,可以說它操作系統里面的緩沖區,請求項,驅動等多個版本都結合起來,想要充分理解整個函數,是十分需要耐心的。
OK,Let GO.
Go
我們看第一次調用bread函數的上下文:
for (drive=0 ; drive<NR_HD ; drive++) {if (!(bh = bread(0x300 + drive*5,0))) {printk("Unable to read partition table of drive %d\n\r",drive);panic("");}if (bh->b_data[510] != 0x55 || (unsigned char)bh->b_data[511] != 0xAA) {printk("Bad partition table on drive %d\n\r",drive);panic("");}p = 0x1BE + (void *)bh->b_data;for (i=1;i<5;i++,p++) {hd[i+5*drive].start_sect = p->start_sect;hd[i+5*drive].nr_sects = p->nr_sects;}brelse(bh);}其中調用語句為bh=bread(0x300+drive*5,0)
傳入的參數有兩個:dev和block。表示讀取設備號為dev,塊號為block的那個塊的內容。
注意這里的0x300+drive*5 ,和式的8位以上的部分0x3是硬盤的主設備號3,低8位是次設備號。
在linux-0.11中,一種存在以下7種塊設備:
注意,其中blk_dev[3]就是被用作硬盤,所以硬盤的主設備號為3.
OK,我們先看bread(0x300,0)這種情況。
bread函數的定義:
struct buffer_head * bread(int dev,int block)//1.有現成的//最新的//不是最新的//2.空閑//3.沒空閑//設備號?塊號?來保證存活時間盡量長?不應該是先進先出 {struct buffer_head * bh;if (!(bh=getblk(dev,block)))panic("bread: getblk returned NULL\n");if (bh->b_uptodate)//緩沖區與硬盤的是不是一致的,return bh;ll_rw_block(READ,bh);//在驅動里面讀wait_on_buffer(bh);if (bh->b_uptodate)return bh;brelse(bh);return NULL; }這個函數的技術路線是:
首先,通過getblk()函數得到一個緩沖區,這個緩沖區通過一個buffer_head的指針來維護。
然后,看這個緩存塊的內容是否是最新的(或者是否已經同步),這里通過bh->b_update來反映。
如果這個緩存塊里面的東西是已經同步過的,那么直接return.
否則,先同步,從磁盤中讀取第一個塊的內容。
到了這里,我們就要先從宏觀上把握緩沖區在內核中作用。
顯然,緩沖區是介于磁盤與進程之間的東西。當進程需要讀取數據的時候,內核先把數據從磁盤中讀到緩沖區里面,然后再從緩沖區里面把數據拷貝到進程空間去。
當進程需要寫數據的時候,內核先把進程要寫的數據寫到緩沖區中,然后內核在把緩沖區的數據寫到硬盤上去。
其中,每個緩沖區由設備號和塊號來標識,一個緩沖區就對應了硬盤上一個塊(這是一個邏輯塊,是操作系統的概念,硬盤不存在塊的概念,在真實寫盤,讀盤的時候,需要把這個塊號映射到硬盤的扇區號上去)。
我們可能會有疑問了,為什么要有緩沖區的存在??為什么硬盤來的數據先放到Buffer中,然后再把數據從Buffer拷到進程中去。
答案就是復用。
這是因為,很有可能,同一個設備號、塊號的內容在一定的時間內被多次使用。例如,同一個進程可以多次讀取同一個塊。考慮到硬盤與內存之間讀取速度的差異,讀一次盤可是需要花費很長時間的,只要能夠撞上一次就撞大發了。
其實,內核緩沖區部分的核心思想就是想方設法讓里面的緩沖塊被復用,那么為了達到這個目的,一種可行的方法就是讓里面的緩沖塊存活的時間盡量的長。直觀的想,存活時間越長,被復用的機會就越大。
OK,我們先看看getblk(dev,block)這個函數。
struct buffer_head * getblk(int dev,int block) {struct buffer_head * tmp, * bh;repeat:if ((bh = get_hash_table(dev,block)))return bh;//找到相同的tmp = free_list;do {if (tmp->b_count)//找引用計數為0的,也就是找空閑的continue;if (!bh || BADNESS(tmp)<BADNESS(bh)) //找最差的,通過dirt,b_lock//dirt:進程有寫這個塊,改了緩沖區//b_lock:1.需要把緩沖區的內存寫入到硬盤; 2.一個進程讀、一個進程寫{bh = tmp;if (!BADNESS(tmp))break;} /* and repeat until we find something good */}while ((tmp = tmp->b_next_free) != free_list);if (!bh)//bh==NULL 說明沒有找到空閑的{sleep_on(&buffer_wait);goto repeat;}wait_on_buffer(bh);//bh里面 dirt,lock有可能為0,這個只是相對更好而已 不是一定最好if (bh->b_count)//上面wait的時候,另外一個進程改了goto repeat;while (bh->b_dirt) {sync_dev(bh->b_dev);wait_on_buffer(bh);if (bh->b_count)goto repeat;} /* NOTE!! While we slept waiting for this block, somebody else might */ /* already have added "this" block to the cache. check it */if (find_buffer(dev,block))goto repeat; /* OK, FINALLY we know that this buffer is the only one of it's kind, */ /* and that it's unused (b_count=0), unlocked (b_lock=0), and clean */bh->b_count=1;bh->b_dirt=0;bh->b_uptodate=0;//???????????remove_from_queues(bh);bh->b_dev=dev;bh->b_blocknr=block;insert_into_queues(bh);return bh; }總結
以上是生活随笔為你收集整理的bread 块设备读取函数解析(1)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于单片机的血压计c语言,基于AT89C
- 下一篇: 【嵌入式】51单片机+1602+dht1