Linux 通用块设备层基础之buffer_head

来源:互联网 发布:js ajax实例 无反应 编辑:程序博客网 时间:2024/04/29 04:59

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来操作数据块的。