日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

bread 块设备读取函数解析(1)

發布時間:2024/3/12 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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種塊設備:

struct blk_dev_struct blk_dev[NR_BLK_DEV] = {{ NULL, NULL }, /* no_dev */{ NULL, NULL }, /* dev mem */{ NULL, NULL }, /* dev fd */{ NULL, NULL }, /* dev hd 主設備號為3*/ { NULL, NULL }, /* dev ttyx */{ NULL, NULL }, /* dev tty */{ NULL, NULL } /* dev lp */ };

注意,其中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)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。