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

在旅途中,我遇见了你,你我相识是缘分!看着你手中的戒指,

Linux 通用块设备层基础之buffer_head

相关文章:

你感兴趣的文章:

标签云: