文件系統的文件太多,而且是照搬的MINIX的文件系統,不想繼續分析下去了。緩衝區機制和文件系統密切相關,所以這裡就簡單分析一下緩衝區機制。 buffer.c 程式用於對高速緩衝區(池)進行操作和管理。高速緩衝區位於內核代碼塊和主記憶體區之間,見圖9-9 中所示。高速緩衝區在塊設備與內核其它程式之間起著 ...
文件系統的文件太多,而且是照搬的MINIX的文件系統,不想繼續分析下去了。緩衝區機制和文件系統密切相關,所以這裡就簡單分析一下緩衝區機制。
buffer.c 程式用於對高速緩衝區(池)進行操作和管理。高速緩衝區位於內核代碼塊和主記憶體區之間,見圖9-9 中所示。高速緩衝區在塊設備與內核其它程式之間起著一個橋梁作用。除了塊設備驅動程式以外,內核程式如果需要訪問塊設備中的數據,就都需要經過高速緩衝區來間接地操作。
因為讀取磁碟數據很耗費時間,所以緩衝區的作用就是存儲讀過的磁碟數據,下次有需求直接從緩衝區讀取,緩衝區是記憶體區域,讀取非常快速。
圖中高速緩衝區的起始位置從內核模塊末段end 標號開始,end 是內核模塊鏈接期間由鏈接程式(ld)設置的一個值,內核代碼中沒有定義這個符號。當在連接生成system 模塊時,ld 程式的digest_symbols()函數會產生此符號。該函數主要用於對全局變數進行引用賦值,並且計算每個被連接文件的其始和大小,其中也設置了end 的值,它等於data_start + datasize + bss_size,也即內核模塊的末段。
整個高速緩衝區被劃分成1024 位元組大小的緩衝塊,正好與塊設備上的磁碟邏輯塊大小相同。高速緩衝採用hash 表和空閑緩衝塊隊列進行操作管理。在緩衝區初始化過程中,從緩衝區的兩端開始,同時分別設置緩衝塊頭結構和劃分出對應的緩衝塊。緩衝區的高端被劃分成一個個1024 位元組的緩衝塊,低端則分別建立起對應各緩衝塊的緩衝頭結構buffer_head(include/linux/fs.h,68 行),用於描述對應緩衝塊的屬性和把所有緩衝頭連接成鏈表。直到它們之間已經不能再劃分出緩衝塊為止,見圖9-10 所示。而各個buffer_head 被鏈接成一個空閑緩衝塊雙向鏈表結構。詳細結構見圖9-11 所示。
緩衝區的大致結構可參照buffer.c的buffer_init函數:
extern int end; // 由連接程式ld 生成的表明程式末端的變數。[??] struct buffer_head *start_buffer = (struct buffer_head *) &end; struct buffer_head *hash_table[NR_HASH]; // NR_HASH = 307 項。 static struct buffer_head *free_list; //// 緩衝區初始化函數。 // 參數buffer_end 是指定的緩衝區記憶體的末端。對於系統有16MB 記憶體,則緩衝區末端設置為4MB。 // 對於系統有8MB 記憶體,緩衝區末端設置為2MB。 void buffer_init (long buffer_end) { struct buffer_head *h = start_buffer; void *b; int i; // 如果緩衝區高端等於1Mb,則由於從640KB-1MB 被顯示記憶體和BIOS 占用,因此實際可用緩衝區記憶體 // 高端應該是640KB。否則記憶體高端一定大於1MB。 if (buffer_end == 1 << 20) b = (void *) (640 * 1024); else b = (void *) buffer_end; // 這段代碼用於初始化緩衝區,建立空閑緩衝區環鏈表,並獲取系統中緩衝塊的數目。 // 操作的過程是從緩衝區高端開始劃分1K 大小的緩衝塊,與此同時在緩衝區低端建立描述該緩衝塊 // 的結構buffer_head,並將這些buffer_head 組成雙向鏈表。 // h 是指向緩衝頭結構的指針,而h+1 是指向記憶體地址連續的下一個緩衝頭地址,也可以說是指向h // 緩衝頭的末端外。為了保證有足夠長度的記憶體來存儲一個緩衝頭結構,需要b 所指向的記憶體塊 // 地址 >= h 緩衝頭的末端,也即要>=h+1。 while ((b -= BLOCK_SIZE) >= ((void *) (h + 1))) { h->b_dev = 0; // 使用該緩衝區的設備號。 h->b_dirt = 0; // 臟標誌,也即緩衝區修改標誌。 h->b_count = 0; // 該緩衝區引用計數。 h->b_lock = 0; // 緩衝區鎖定標誌。 h->b_uptodate = 0; // 緩衝區更新標誌(或稱數據有效標誌)。 h->b_wait = NULL; // 指向等待該緩衝區解鎖的進程。 h->b_next = NULL; // 指向具有相同hash 值的下一個緩衝頭。 h->b_prev = NULL; // 指向具有相同hash 值的前一個緩衝頭。 h->b_data = (char *) b; // 指向對應緩衝區數據塊(1024 位元組)。 h->b_prev_free = h - 1; // 指向鏈表中前一項。 h->b_next_free = h + 1; // 指向鏈表中下一項。 h++; // h 指向下一新緩衝頭位置。 NR_BUFFERS++; // 緩衝區塊數累加。 if (b == (void *) 0x100000) // 如果地址b 遞減到等於1MB,則跳過384KB, b = (void *) 0xA0000; // 讓b 指向地址0xA0000(640KB)處。 } h--; // 讓h 指向最後一個有效緩衝頭。 free_list = start_buffer; // 讓空閑鏈表頭指向頭一個緩衝區頭。 free_list->b_prev_free = h; // 鏈表頭的b_prev_free 指向前一項(即最後一項)。 h->b_next_free = free_list; // h 的下一項指針指向第一項,形成一個環鏈。 // 初始化hash 表(哈希表、散列表),置表中所有的指針為NULL。 for (i = 0; i < NR_HASH; i++) hash_table[i] = NULL; }
根據上面的理論知識,這段代碼很好分析,就不多做解釋了。