块设备处理
时间:2006-11-14 来源:hoog
本文摘译自<<深入理解Linux内核>>第三版
以一个进程发起read()系统调用为例,我们来看看内核是如何来处理这个系统的请求的.
1,read()系统调用的服务例程激活相应的VFS层函数,并将文件描述符和偏移量作为参数同时传递给VFS层的函数.VFS(虚拟文件系统)是块设备处理体系中的最上层,它为Linux支持的所有文件系统提供通用的文件模型.
图: 被块设备所影响的内核部件
500)this.width=500;" border=0>
2,VFS函数决定请求的数据是否可用和如何执行read操作.在某些情况下,VFS函数并不需要访问磁盘数据,因为内存中有内核保存的最近读自磁盘或者写往磁盘的数据.
3,让我们假设内核必须从磁盘设备读取数据,因此它必须决定数据的物理位置.而为了做到这一点,内核不得不依赖映射层(mapping layer),它主要执行下面的两步:
a,它测定包括这个文件的文件系统的块大小并且计算使用文件块号来计算请求数据的范围.本质上讲,文件被看做被划分为许多的块(block),内核确定包含请求数据的文件块块数.
b,下一步,映射层调用具体文件系统的函数来访问文件的磁盘节点(disk inode)并使用逻辑磁盘号(logical block numbers)来确定请求数据在磁盘的位置.本质上,磁盘被看做划分成块,而内核确定磁盘存储数据的块数.因为一个文件可能在磁盘上并不以连续的方式存储,所以每个磁盘节点里面存储的数据结构都把每个文件块号映射为逻辑块号.
4,内核现在可以发起磁盘设备的read操作了.它利用通用块层(generic block layer).通用块层发起一个传输请求数据的I/O操作.总体上,每个I/O操作调用在磁盘上相邻的一组块.因为请求数据并不需要在磁盘上保持连续,所以,通用块层(generic block layer)可能发出好几次的I/O操作.每次的I/O操作用”块I/O(block I/O)”结构描述.这个结构收集底层部件执行这次请求所需要的所有信息.
通用块层(generic block layer)隐藏了每种硬件磁盘设备的特性,因此提供了一种块设备的抽象试图(abstract view).因为绝大多数的块设备是磁盘,通用块层(generic block layer)也提供一些通用数据结构来描述”磁盘”和”磁盘分区”.
5.在通用块层(generic block layer)之下,”I/O调度器(I/O scheduler)”根据预先定义好的内核策略来分类所有没有执行的I/O数据传输请求.调度器的目的是把在物理介质上彼此相邻的数据请求归成一组.
6.最后,块设备驱动通过发出具体的命令到磁盘控制器的硬件接口来负责实际的数据传输.
就像你所看到的,有许多内核部件关注存储在块设备中的数据;它们使用各种不同的长度和称谓来管理磁盘数据.
l 硬件块设备控制器使用一种叫做”扇区(sectors)”的东西来传递固定长度的数据.因此,I/O调度器和块设备驱动必须以管理扇区的里面的数据.
l 虚拟文件系统(VFS),映射层(mapping layer),和文件系统使用一种叫做”块(blocks)”的逻辑但愿来分组磁盘数据.一个块的对应与文件系统中最小的磁盘存储单位.
l 我们很快将看到,块设备驱动必须能够处理”段(segments)”大小的数据;每个段是一个内存页或者包含有磁盘上物理相邻的一大堆数据的内存页的一部分.
l 磁盘缓存(disk caches)工作在以”页(pages)”为大小的磁盘数据上,每一个页都对应一个页帧(page frame).
l 通用块层(generic block layer)把所有的上层的和下层的部件粘合起来,因此它必须懂得扇区,块,段,和页的数据.
甚至如果有许多不同大小的数据,它们通常共享同样的物理内存单元(physical RAM cells).例如,下图所示的4096-byte的页的层次图.上层的内核部件把一页视为四个块缓存(每个1024byte大小)的集合.这个页的最后三个块被块设备驱动传输,因此它们被一个3072bytes的段包含.磁盘控制器视这个段是6个512byte大小的扇区的集合.