内核知识收集
1. 概述
在块设备上的操作,涉及内核中的多个组成部分。下图
系统调用read()读取磁盘上的文件。内核响应步骤如下:
a. 系统调用read()传递文件描述符/文件偏移量/读取长度等参数, 触发相应的VFS函数。
b. VFS确定请求的数据是否已经在缓冲区中, 若数据不在缓冲区中, 确定如何执行读块设备操作。
内核将大多数最近从块设备读出或写入的数据保存在RAM,所以有时候没必要访问磁盘上的数据。
c. 内核通过映射层(Mapping Layer)确定数据在物理设备上的位置。由对应文件所在的文件系统(ext4/reiserfs/xfs等)来确定。
1>内核取得文件所在文件系统的块大小,计算所请求数据块的长度,确证数据所在块号。
2>映射层调用具体文件系统的函数,访问磁盘节点,根据逻辑块号确定所请求数据在磁盘上的位置。
3>磁盘也分块,内核须确定数据块(通用块)在磁盘上的块号(磁盘块)。
d. 内核通过通用块层(Generic Block Layer)在块设备上执行读操作,启动I/O操作, 传输请求的数据。
1>一个i/o操作只针对一组连续的块,请求的数据不必位于相邻的块中。
2>通用块层为所有的块设备提供一个抽象视图,隐藏硬件块设备的差异,可用通用数据结构描述。
e. 内核I/O调度层(I/O Scheduler Layer)根据内核的调度策略, 对等待处理的I/O等待队列排序。
把物理介质上相邻的数据请求聚集在一起。
f. 块设备驱动(Block Device Driver)通过向磁盘控制器发送相应的命令,执行真正的数据传输。
这里讲述上面步骤d通用块层的工作过程
2. 块设备相关概念
能够随机访问固定大小数据片(chunk)的设备称为块设备, 这些数据片就称作块。
最常见的块设备是硬盘,除此之外,还有软盘驱动器、CD-ROM驱动.
它们都是以安装文件系统的方式使用的。这也是块设备通常的访问方式。
a. 扇区(Sectors)
块设备中最小的可寻址单元是扇区。
扇区大小一般是2的整数倍,而最常见的大小是512字节。
扇区的大小是设备的物理属性,扇区是所有块设备的基本单元—块设备无法对比它还小的单元进行寻址和操作,不过许多块设备能够一次传输多个扇区。虽然大多数块设备的扇区大小都是512字节,不过其他大小的扇区也很常见(比如,很多CD-ROM盘的扇区都是2k大小)。若块设备使用的扇区大于512字节,则相应的底层驱动程序负责相应的转换工作。
数据在块设备上的位置由块索引和块内偏移确定。块索引(sector indices)在32或64位系统上的变量类型为sector_t。
b. 块(Blocks)
对于硬件设备来说,扇区是基本的数据传输单元;而对于VFS(虚拟文件系统),块(block)是基本的数据传输单元。
例如,当内核访问文件的数据时,它首先从磁盘上读取一个块,这个块有文件的inode,该块对应于磁盘上一个或多个扇区。
扇区是块设备的最小可寻址单元, 块不能比扇区还小,只能整数倍于扇区大小。
另外内核(对有扇区的硬件设备)还要求块大小是2的整数倍,而且不能超过一页的长度。
所以对块大小的最终要求是,必须是扇区大小的2的整数倍,并且要小于页面大小。所以通常块大小是512字节、1K或4K。
至于块(block)大小,与具体的硬件设备无关。在块设备上创建文件系统时,管理员可以选择块大小;因此在同一个磁盘上,多个分区可能使用不同的块大小。
c. 段(Segments)
每个磁盘I/O操作都是设备上相邻的扇区与内存之间传输数据。
在大多数情况下,数据的传输通过DMA方式;块设备驱动向磁盘控制器发起命令,触发数据传输;
当数据传输结束时,控制器给块设备驱动发送一个中断。
简单的DMA操作,只能传输磁盘上相邻的扇区。这是物理属性的限制:若磁盘控制器DMA传输在非连续的扇区上,则会导致性能下降;因为在磁盘表面上移动读/写磁头,是相当耗时的工作。
现在的磁盘控制器支持“分散/聚合”(scatter-gather)DMA操作,这种操作模式下,数据传输可以在多个非连续的内存区域中进行。
对于每个“分散/聚合”DMA操作,块设备驱动向磁盘控制器发送:
1>.初始磁盘扇区号和传输的总共扇区数;
2>.内存区域的描述链表,每个描述都包含一个地址和长度。
磁盘控制器来管理整个数据传输;例如,在读操作中,控制器从磁盘相邻的扇区上读取数据,然后将数据分散存储在内存的不同区域。
为了利用“分散/聚合”DMA操作,块设备驱动必须能处理被称为段(segments)的数据单元。
一个段就是一个内存页面或一个页面的部分,它包含磁盘上相邻扇区的数据。这样一个“分散/聚合”DMA操作可能会涉及多个段。
在通用块设备层(generice block layer)中,若多个页面在内存中连续,且相应的磁盘数据在磁盘上也相邻,那么可以将多个段合并。合并操作产生的大内存区域称为物理段(physical segment)。
3. 通用块设备层
通用块设备层(Generic Block Layer)是内核的一个组成部分,它处理系统所有对块设备的请求。
有通用块设备层提供的函数,内核可以方便地做到:
1>将数据存放在高端内存—仅当CPU访问高端内存的数据时,才将页框映射到内核的线性地址空间, 访问完成后解除映射。
2>实现零拷贝(zero-copy), 即磁盘数据直接拷贝到用户地址空间, 而不需要先拷贝到内核地址空间。
实际上是,内核进行I/O数据传输使用的页面被映射到用户进程的地址空间中
3>管理逻辑卷, 如LVM(Logical Volume Manager)和RAID(Redundant Array of Inexpensive Disks)使用的逻辑卷。
几个不同块设备上的分区,也可以被看作一个单一的分区
4>发挥现在的磁盘控制器新特性,如大的磁盘缓存、增强的DMA功能、I/O请求调度等。
a. bio结构体
内核中块I/O操作的基本容器由bio结构体表示,它定义在文件include/linux/bio.h中。
该结构体代表了正在活动的以段(segment)链表形式组织的块I/O操作。
一个段是一小块连续的内存缓冲区。这样,单个缓冲区就不一定要连续。
所以使用段来描述缓冲区,即使一个缓冲区分散在内存的多个位置上,bio结构体也能对内核保证I/O操作的执行。
这样的向量I/O称为分散-聚合I/O。
bio结构体中的主要成员变量都是用来管理
I/O操作执行的相关信息的,其中最重要的几个成员变量是bi_io_vecs、bi_vcnt和bi_idx。 下图显示了bio结构体及相关结构体之间的关系。
b. 磁盘和磁盘分区的表示
4. 通用块设备层对请求的处理