上次看sys_read()看得太乱了,主次不分,于是重新看了遍,对流程有了个大致的了解:
read()系统调用所涉及的内核组件如上图所示,依次为vsf层、磁盘高速缓存、映射层、通用块设备层、I/O调度层、磁盘设备驱动。 作用大致如下: vfs层决定如何执行read操作,将filp->f_op->read指向具体的函数; 内核映射层确定数据的物理位置,主要执行两步:1、确定数请求数据所在的块号;2调用具体的文件系统的函数,访问磁盘索引节点,根据逻辑块号来确定请求数 据在磁盘上的位置; 通用块层将上层的请求构造成bio结构,再将bio结构提交个I/O调度层; I/O调度层主要的工作则将上层的bio构造成request结构,将其加入到对应的request_queue中,并完成合并和排序的工作; 块设备从request中取得请求,然后再完成这些请求。
vfs层
映射层
filp->f_op->read和filp->f_op->aio_read指向具体的文件系统的指针,在 ext2文件系统中这两者分别对应generic_file_read()和generic_file_aio_read(),而 generic_file_read()和generic_file_aio_read()都将调用 __generic_file_aio_read()。
(注:大多数磁盘文件系统的read方法是由generic_file_read()通用函数实现,而 __generic_file_aio_read()函数是所有文件系统实现同步和异步读操作所使用的通用例程。此外,对于大多数问价系统来说,从文件中 读取一个数据页就等同于在磁盘上查找所请求的数据存放在哪些块上。只要这个过程完成了,内核就可以通过向通用块层提交适当的I/O操作来填充这些页)
通用块设备层
I/O调度层
接下来说明q->make_request的赋值:
在块设备驱动程序中(如sbull中),会调用blk_init_queue()来分配并初始化一个request队列,"prepare a request queue for use with a block device";
blk_init_queue()函数调用函数blk_init_queue_node(),blk_init_queue_node() 的主要工作就是对request队列进行初始化,在这里我只关注两个函数request_fn()和make_request_fn(),前者会被赋值为 blk_init_queue()的第一个参数,而make_request_fn()则会由blk_queue_make_request()设置为 __make_request()。
__make_request()函数会完成合并、排序等操作。
块设备驱动层
request弄好了之后,使用了blk_init_queue()块设备驱动程序就会通过request_fn()来处理器请求了。
在sbull中使用了两种模式:RM_SIMPLE、RM_FULL。