Chinaunix首页 | 论坛 | 博客
  • 博客访问: 407212
  • 博文数量: 128
  • 博客积分: 2247
  • 博客等级: 大尉
  • 技术积分: 767
  • 用 户 组: 普通用户
  • 注册时间: 2010-06-17 09:30
文章分类

全部博文(128)

文章存档

2011年(4)

2010年(124)

我的朋友

分类:

2010-07-28 12:30:49

系统中能够随机访问固定大小数据片(chunk)的设备被称作块设备,这些数据片就称作块,最常见的块设备是硬盘。块设备都是以安装文件系统的方式使用的——这也是块设备通常的访问方式。

      另一种基本的设备类型是字符设备。字符设备按照字符流的方式被有序访问,像串口和键盘就属于字符设备。

      这两种类型的设备的根本区别在于它们是否可以被随机访问——换句话说,就是能否在访问设备时随意的从一个位置跳转到另一个位置。比如说,硬盘设备的驱动可 能需要读取磁盘上任意块的内容,然后又转去读取别的块的内容,而被读取的块在磁盘上位置不一定要连续,所以说硬盘可以被随机访问,而不是以流的方式访问, 显然它是一个块设备。

13.1 解剖一个块设备

      块设备中最小的可寻址单元是扇区。扇区大小一般是2的整数倍,而最常见的大小是512个字节。扇区的大小是设备的物理属性,扇区是所有块设备的基本单元——块设备无法对比它还小的单元进行寻址和操作,不过许多块设备能够一次就传输多个扇区。

       虽然各种软件的用途不同,但是它们都会用到自己的最小逻辑单元——块。块是文件系统的一种抽象——只能基于块来访问文件系统。虽然物理磁盘寻址是按照扇区级进行的,但是内核执行的所有磁盘操作都是按照块进行的。

       扇区对内核的重要性在于所有设备的I/O操作都必须基于扇区来进行;反过来,块是内核使用的较高层概念,它是比扇区高一层的抽象。

13.2   缓冲区和缓冲区头

      当一个块被调入内存(也就是在读入或等待写出)时,它要存储在一个缓冲区中。每个缓冲区与一个块对应,它相当于是磁盘块在内存中的表示。块包含一个或多个 扇区,但大小不能超过一个页面,所以一个页可以容纳一个或多个内存中的块。由于内核在处理数据时需要一些相关的控制信息,所以每个缓冲区都有一个对应的描 述符。该描述符用buffer_head结构体来表示,被称作缓冲区头,在文件中定义,它包含了 内核操作缓冲区所需要的全部信息。

      下面给出缓冲区头结构体和其中各个域的说明:

struct   buffer_head {

                 ufsigned long         b_state;          /* 缓冲区状态标志 */

                atomic_t                 b_count;        /*   缓冲区使用计数 */

               struct buffer_head   *b_this_page     /* 叶茂中的缓冲区   */

               struct   page           *b_page;        /*   存储缓冲区的页面   */

               sector_t                   b_blocknr;      /* 逻辑块号 */

               u32                        b_size;            /* 块大小 */

               char                        *b_data;           /* 页面中的缓存区 */

              struct             block_device    *b_bdev;       /*   块设备 */

             bh_end_io_t      *b_end_io;                    /* I/O完成方法   */

             void                   *b_private;                   /*   完成方法数据    */

             struct list_head         b_assoc_buffers;      /* 相关的映射链表 */

   };

       b_state域表示缓冲区的状态,合法的标志存放在bh_state_bits枚举中,该枚举在中定义。

BH_Uptodata             该缓冲区包含可用数据

BH_Dirty              该缓冲区是脏的(缓存中的内容比磁盘中的块内容新,所以缓冲区内容必须被写回磁盘)

BH_Lock             该缓冲区正在被I/O操作使用,被锁定以防被并发访问

BH_Red              该缓冲区有I/O请求操作

BH_Mapped       该缓冲区是映射磁盘块的可用缓冲区

BH_New            缓冲区是通过get_block()刚刚映射的,尚且不能访问

BH_Async_Read       缓冲区正在通过end_buffer_asnc_read()被异步I/O读操作使用

BH_Async_Write        该缓冲区正在通过end_buffer_async_write()被异步I/O写操作使用

BH_Delay                  该缓冲区尚未和磁盘块关联

BH_Boundary           该缓冲区处于连续块区的边界——下一个块不再连续

       bh_state_bits列表还包含了一个特殊标志——BH_PrivateStart,该标志不是可用状态标志,使用它是为了指明可被其他代码使用的 起始位。块I/O层不会使用BH_PrivateStart或更高的位。驱动程序可以在这些位中定义自己的状态标志,只要保证自定义的状态标志不与块I /O层的专用位发生冲突就行。

显然一个缓冲区可以同时具有上述状态的几种。

块高速缓存的管理很复杂,下面先对空缓冲区、空闲缓冲区、正使用的缓冲区、缓冲区的大小以及缓冲区的类型作一个简短的介绍:

缓冲区可以分为两种,一种是包含了有效数据的,另一种是没有被使用的,即空缓冲区。

具有有效数据并不能表明某个缓冲区正在被使用,毕竟,在同一时间内,被进程访问的缓冲区(即处于使用状态)只有少数几个。当前没有被进程访问的有效缓冲区和空缓冲区称为空闲缓冲区。其实,buffer_head结构中的b_count就可以反映出缓冲区是否处于使用状态。如果它为0,则缓冲区是空闲的。大于0,则缓冲区正被进程访问。

缓冲区的大小不是固定的,当前Linux支持5种大小的缓冲区,分别是5121024204840968192字节。Linux所支持的文件系统都使用共同的块高速缓存,在同一时刻,块高速缓存中存在着来自不同物理设备的数据块,为了支持这些不同大小的数据块,Linux使用了几种不同大小的缓冲区。

b_count域表示缓冲区的使用计数,当它为0时,说明缓冲区是空闲的。        

与缓冲区对应的磁盘物理块由b_blocknr域索引,该值是b_bdev域指明的块设备中的逻辑块号。

与 缓冲区对应的内存物理页由b_page域表示,另外,b_data域指向相应的块(它位于b_page域所指明的页面中的某个位置上),块的大小由 b_size域表示,所以块在内存中的起始位置在b_data处,结束位置在(b_data+b_size)处。       

缓 冲区头的目的在于描述磁盘块和物理内存缓冲区之间的映射关系,这个结构体在内核中只扮演一个描述符的角色,说明从缓冲区到块的映射关系。但是仅仅把磁盘块 映射到物理内存是不够的,还要对映射过来的数据进行操作啊(要不然映射过来干什么呢),下面的bio结构体就实现了块I/O操作。 换句话说,缓冲区头负责映射,而bio负责操作,前者要面对的是磁盘块和内存缓冲区,后者只需面对缓冲区就可以了。      13.3   bio结构体

目 前内核中块I/O操作的基本容器由bio结构体表示,它定义在文件中。该结构体代表了正在现场的(活动的)以片段 (segment)链表形式组织的块I/O操作。一个片段是内存中一块连续小区域。这样的话,就不需要保证单个缓冲区一定要连续。所以通过用片段来描述缓 冲区,即使一个缓冲区分散在内存的多个位置上,bio结构体也能够保证I/O操作的执行。

struct bio {
sector_t   bi_sector;                          /*   I/O的起始扇区 */
struct bio   *bi_next;                         /* request queue link */
struct block_device *bi_bdev;          /* 指向实际执行的I/O设备 */
unsigned long   bi_flags;                  /* status, command, etc */
unsigned long   bi_rw;                       /* bottom bits READ/WRITE,   top bits priority */

unsigned short   bi_vcnt;             /* how many bio_vec's */
unsigned short   bi_idx;                        /* current index into bvl_vec */

unsigned short   bi_phys_segments;    /* Number of segments in this BIO after
                                                                     * physical address coalescing is performed. */
   unsigned short   bi_hw_segments;              /* Number of segments after physical and DMA remapping
                                                                      * hardware coalescing is performed. */
unsigned int   bi_size;                                 /* residual I/O count */

/*
* To keep track of the max hw size, we account for the
* sizes of the first and last virtually mergeable segments
* in this bio
*/
unsigned int   bi_hw_front_size;
unsigned int   bi_hw_back_size;

unsigned int   bi_max_vecs;            /* max bvl_vecs we can hold */

struct bio_vec   *bi_io_vec;               /* the actual vec list */

bio_end_io_t   *bi_end_io;
atomic_t   bi_cnt;                                         /* pin count */

void    *bi_private;

bio_destructor_t                       *bi_destructor; /* destructor */
};
      使用bio结构体的目的主要是代表正在现场执行的I/O操作,所以该结构体中的主要域都是用来管理相关信息的,其中最重要的几个域是 bi_io_vecx、bi_vcnt和bi_idx。bi_io_vecs指向一个bio_vec结构体数组,该结构体链表包含了一个特定I/O操作所 需要使用到的所有片段。每个bio_vec结构都是一个形式为的向量,它描述的是一个特定的片段:片段所在的物理页、块在的物理页中的偏移量、从给定偏移量开始的块的长度。整个bio_io_vec结 构体数组表示了一个完整的缓冲区。bio_vec结构定义在文件中。

在每个给定的块I/O操作中,bi_vcnt域用来描述bi_io_vec所指向的bio_vec数组中的向量数目。当块I/O操作执行完成后,bi_idx域指向数组的当前索引。

每个块I/O请求都通过一个bio结构体表示。每个请求包含一个或多个块,这些块存储在bio_vec结构体数组中。这些结构体描述了每个片段在物理页中的实际位置,并且像向量一样被组织在一起。                 13.4   请求队列

块 设备将它们挂起的块I/O请求保存在请求队列中,该队列由reques_queue结构体表示,定义在文件 中,包含一个双向请求链表以及相关控制信息。通过内核中像文件系统这样高层的代码将请求加入到队列中。请求队列只要不为空,队列对应的块设备驱动程序就会 从队列头获取请求,然后将其送入对应的块设备上去。请求队列表中的每一项都是一个单独的请求,由request结构体表示。

请求

队 列中的请求由结构体request表示,它定义在文件中。因为一个请求可能要操作多个连续的磁盘块,所以每 个请求可以由多个bio结构体组成。注意,虽然磁盘上的块必须连续,但是在内存中这些块并不一定要连续——每个bio结构体都可以描述多个片段(回忆一 下,片段是内存中连续的小区域),而每个请求也可以包含多个bio结构 体。                                                                                     13.5   小结

讨论了块设备的基本知识,考察了块I/O层所用到的数据结构:bio,表示活动的I/O操作;buffer_head,表示块到页的映射;还有请求结构,表示具体的I/O请求。       关于I/O请求和bio的关系,没有深入的了解。

阅读(1008) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~