Linux 通用塊設備層基礎之buffer_head

1. 塊設備

Linux 系統中能夠隨機訪問的數據片(chunk)的設備稱爲塊設備,這些數據片稱爲片。而字符設備是按照字符流的方式有序訪問。常見的塊設備如硬盤,CD-ROM,而字符設備主要有串口和鍵盤。塊設備最小可尋址的單元稱爲扇區,通常情況下,扇區的大小爲512個字節。而文件系統最小邏輯可尋址單元稱爲塊。塊的大小要比扇區大,但比頁小,一般爲512,1K,或者是4K. 內核執行磁盤的所有操作是按照塊來操作的,又稱爲"文件塊"或者是"I/O塊"。

2. 緩衝區與緩衝區頭

當一個塊被調入到內存中,它要被存儲在一個緩衝區中。每個緩衝區與一個塊對應,它相當於磁盤塊在內存中的表示。而文件在內存中由file結構體表示。由於內核處理塊時需要一些信息,如塊屬於哪個設備與塊對應於哪個緩衝區。所以每個緩衝區都有一個緩衝區描述符,稱爲buffer_head. 它包含了內核操作緩衝區所需要的全部信息,在Linux- 2.6.20下的<linux/buffer_head.h>文件中.

具體的結構如下:

/*
 * Historically, a buffer_head was used to map a single block
 * within a page, and of course as the unit of I/O through the
 * filesystem and block layers.  Nowadays the basic I/O unit
 * is the bio, and buffer_heads are used for extracting block
 * mappings (via a get_block_t call), for tracking state within
 * a page (via a page_mapping) and for wrapping bio submission
 * for backward compatibility reasons (e.g. submit_bh).
 */
struct buffer_head {
    unsigned long b_state;        /* buffer state bitmap (see above) *緩衝區的狀態標誌/
    struct buffer_head *b_this_page;/* circular list of page's buffers *頁面中緩衝區/
    struct page *b_page;        /* the page this bh is mapped to *存儲緩衝區的頁面/

    sector_t b_blocknr;        /* start block number *邏輯塊號/
    size_t b_size;            /* size of mapping *塊大小/
    char *b_data;            /* pointer to data within the page *指向頁面中數據的指針/

    struct block_device *b_bdev;       //對應的塊設備
    bh_end_io_t *b_end_io;        /* I/O completion */
     void *b_private;        /* reserved for b_end_io *I/O完成的方法/
    struct list_head b_assoc_buffers; /* associated with another mapping */
    struct address_space *b_assoc_map;    /* mapping this buffer is
                           associated with *緩衝區對應的映射,即address_space/
    atomic_t b_count;        /* users using this buffer_head *表示緩衝區的使用計數/
};
b_state 表示緩衝區的狀態,合法的標誌存放在 bh_state_bits中,該枚舉在<linux/buffer_head.h>中定義。

enum bh_state_bits {
    BH_Uptodate,    /* Contains valid data *該緩衝區包含可用數據/
    BH_Dirty,    /* Is dirty 該緩衝區是髒的,緩衝區的內容比磁盤中的塊內容要新,所以緩衝區的內容必須被寫回磁盤*/
    BH_Lock,    /* Is locked 該緩衝區正在被I/O操作訪問,被鎖定以防止併發訪問*/
    BH_Req,        /* Has been submitted for I/O *該緩衝區有I/O請求操作/
    BH_Uptodate_Lock,/* Used by the first bh in a page, to serialise
              * IO completion of other buffers in the page
              */

    BH_Mapped,    /* Has a disk mapping 該緩衝區是映射磁盤塊的可用緩衝區*/
    BH_New,        /* Disk mapping was newly created by get_block *緩衝區是通過get_block()剛剛映射的,尚且不能訪問/
    BH_Async_Read,    /* Is under end_buffer_async_read I/O 該緩衝區正通過end_buffer_async_read()被異步I/O讀操作使用*/
    BH_Async_Write,    /* Is under end_buffer_async_write I/O *該緩衝區正通過end_buffer_async_write()被異步寫操作使用/
    BH_Delay,    /* Buffer is not yet allocated on disk *該緩衝區尚未與磁盤塊關聯/
    BH_Boundary,    /* Block is followed by a discontiguity *該緩衝區片於連續塊區的邊界,下一個塊不再連續/
    BH_Write_EIO,    /* I/O error on write */
    BH_Ordered,    /* ordered write */
    BH_Eopnotsupp,    /* operation not supported (barrier) */

    BH_PrivateStart,/* not a state bit, but the first bit available
             * for private allocation by other entities
             */
};


注意:
在操作的緩衝區頭之前,應該先使用get_bh()函數增加緩衝區頭的引用計數,確保該緩衝區頭不會再被分配出去,當完成緩衝區頭的操作之後,還必須使用put_bh函數減少引用計數。與緩衝區頭對應的磁盤物理塊由b_blocknr索引,該值是b_bdev域指明的塊設備的邏輯塊號。與緩衝區對應的內存物理頁由b_page表示. b_data直接指向相應的塊(它位於b_page所指明的頁面上的某個位置),塊的大小由b_size表示。所以塊在內存中的起始位置在b_data處,結束位置在(b_data+b_size處。 緩衝區頭的目的在於描述磁盤塊和物理內存緩衝區之間的映射關係. 在2.6以前,緩衝區頭的作用比現在還重要。因爲緩衝區頭作爲內核I/O操作單元,不僅僅描述了從磁盤塊到物理內存的映射,而且還是所有塊I/O操作的窗口。
但會帶來問題:
(1)對內核來說更傾向操作頁面,用一個巨大的緩衝區頭表示每一個獨立的緩衝區效率低下,對緩衝區頭的操作不方面
(2)它僅描述單個緩衝區,當作爲所有I/O容器使用時,它會使內核打斷對大塊數據的I/O操作,使其成爲對多個buffer_head結構體進行操作,所以引入bio,對塊進行操作

總結:

緩衝區:磁盤塊在物理內存中的表示形式

緩衝區描述符:對緩衝區的相關信息的描述,描述了緩衝區與磁盤塊的映射關係

bio(塊I/O):真正的磁盤塊操作用bio來表示,無論是經過頁面高速緩存的I/O還是直接I/O,都是用bio來操作數據塊的。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章