总结
结构: vfs->(磁盘高速缓存)->磁盘文件系统->通用块层->i/o调度程序->块设备驱动->hardware
磁盘文件系统层是个map layer, 比如ext, fat等, 将文件拆分成多块, 根据索引节点, 将每个文件块号映射为一个逻辑块号。然后调用通用块层, 每个io操作只针对磁盘上的一组连续的块, 由于请求不必相邻, 所以通用块层可能启动几次io操作, 每次io用bio描述。 io调度程序是将待处理的io数据按照策略进行归类。然后再通过驱动程序向硬件发送适当的命令。
i/o调度程序/块设备驱动块设备驱动程序用扇区处理数据
块设备驱动程序能用段处理数据, 一个段是一个内存页或者一部分
vfs/map layer(fs)用块处理数据
磁盘高速缓存在页上
通用块层转换扇区,块, 段, 页
扇区是基本单位(linux是512字节, 但硬件设备可能使用1024, 2048, 需要转换), 块包含1个或多个扇区, 且不能超过一个页框, 文件系统的块大小可以设定。 段是给dma传送用的, 一个段就是一个内存页或者一部分,包含几个块。 如果不同的段在内存中是连续的, 在磁盘也是相邻的, 就可以合并, 这种区域称为物理段。
逻辑卷管理器(lvm), 廉价磁盘冗余阵列(raid)使用的逻辑卷:几个磁盘分区, 即使位于不同的快设备, 也可看成一个单一的分区。
bio中每个段由bio_vec数据结构描述:成员bv-page: 所在页描述符指针, bv_len:段字节长度, bv_offset: 页框中段的偏移量
bio的成员 bio_vec指针:指向bio_vec数据结构的第一个元素, bi_vcnt保存bio_vec数组的当前个数
一个bio可以有几个bio_vec段
gendisk中的几个字段:
policy: 1: 只读的, 否则为0
in_flight: 正在进行的i/o操作数
block_device_operation:
ioctl: 使用大内核锁
compat_ioctl: 不使用大内核锁
当向通用块层递交i/o操作请求的时候, 内核所执行的步骤:
先分配bio, 并初始化,
然后调用generic_make_request: 获取与块设备相关的请求队列q, 等待i/o调度程序,如果指向的是分区, 调整bio到整盘绝对地址, 调用queue的make_request_fn将bio请求插入请求队列(电梯调度) 。
通用块层调用i/o调度程序产生一个新的块设备请求或扩展一个已有的快设备请求, 然后终止, 随后激活的块设备驱动程序会调用一个策略例程选择一个待处理的请求, 并向磁盘控制器发出适当的命令满足请求,当io终止, 磁盘控制器发出中断, 如有需要, 中断处理会调用策略例程处理另一个请求。
open: dentry_open会调用blkdev_open(0:
调用bd_acquire()获得块描述符block_device bdev(可以指向整盘, 也可以是分区)。先看inode的i_bdev是否为空, 非空, 返回这个, 为空, 调用bdget获取这个block_device(如果没有就创建一个, 用bdget是有时候别的设备打开了这个设备, 就会在bdev特殊文件系统中留下信息, blockdev_superblock是bdev的超级块),
调用get_gendisk获得gendisk, 如是分区, 返回分区号。
如果打开的是整盘, 调用gendisk的open(驱动定义), 获取盘的参数, 包括分区信息
write: do_sync_write也会调用aio_write:
__generic_file_aio_write:
如果是O_DIRECT, 调用generic_file_direct_write, 否则调用generic_file_buffered_write(有个高速页缓存机制),
这些会调用不同文件系统定义的address_space的write_page(s), direct_io函数, 比如ext2, 会调用ext2_writepage, ext2_direct_io等, 会调用submit_bh->submit_bio->generic_make_request()(direct_io会直接调用到submit_bio)
generic_file_buffered_write:这个调用address_space的write_begin/write_end(不同文件系统注册)
页高速缓存存下列数据:
1. 普通文件
2. 含目录的页
3. 未通过文件系统, 直接从块设备读出的页
4. 进程交换的页
5. 特殊文件系统的页, 如ipc所用的shm
设备文件和普通文件的不同主要在于各自的inode不同, 普通文件的inode指向数据以及对应文件系统的操作方法, 而设备文件不指向数据块, 对应的操作方式是字符设备和块设备的缺省方法。 虽然设备文件也是具体文件系统的文件。
页高速缓存由address_space对象表示, 用于描述块设备的页数据缓存。由缓冲区首部(bh)来寻址。
vfs首先在页高速缓存中查找数据, 没有的话, 发起块设备读写, 并更新缓冲区。
对于每个设备, udev根据sysfs的dev来产生设备文件, 在系统 初始化或者热插拔时。