全部博文(2759)
分类: LINUX
2013-11-20 12:32:15
原文地址:跟踪系统调用之旅-续2 作者:woaimaidong
函数generic_file_read()通过执行宏init_sync_kiocb来初始化描述符kiocb,并设置一个同步操作对象的有关字段。具体地说就是,改宏设置ki_key字段为KIOCB_SYNC_KEY,ki_filp字段为filp、ki_obj字段为current。
然后将刚填好的iovec和kiocb描述符地址传给__ generic_file_aio_read()。函数__ generic_file_aio_read()是所有文件系统实现同步和异步操作所使用的通用例程。
如果是直接 io (filp->f_flags 被设置了 O_DIRECT 标志,即不经过 cache)的方式,则调用 generic_file_direct_IO 函数;如果是 page cache 的方式,则调用 do_generic_file_read 函数。函数 do_generic_file_read 仅仅是一个包装函数,它又调用 do_generic_mapping_read 函数,此函数在文件include/linux/fs.h中。
do_generic_mapping_read函数在mm/filemap.c文件中,do_generic_mapping_read函数主要流程如下:
readpage 函数在include/linux/fs.h,变量 mapping 为 inode 对象中的 address_space 对象,address_space 对象是嵌入在 inode 对象之中的,那么不难想象: address_space 对象成员 a_ops 的初始化工作将会在初始化 inode 对象时进行,如下图:
变量 ext2_aops 或者变量 ext2_nobh_aops的定义如下:
readpage 成员都指向函数 ext2_readpage,so: 函数 do_generic_mapping_read 最终调用 ext2_readpage 函
数处理读数据请求。ext2_readpage函数在fs/ext2/inode.c文件中
mpage_readpage()函数在fs/mpage.c
该函数首先调用函数 do_mpage_readpage 函数创建了一个 bio 请求,该请求指明了要读取的数据块所在磁盘的位置、数据块的数量以及拷贝该数据的目标位置——缓存区中 page 的信息。然后调用 mpage_bio_submit 函数处理请求。 mpage_bio_submit 函数则调用 submit_bio 函数处理该请求,后者最终将请求传递给函数 generic_make_request ,并由 generic_make_request 函数将请求提交给通用块层处理。
__generic_make_request根据bio中保存的设备号取得请求队列q,检查当前IO调度器是否可用,如果可以,继续;否则等待调度器可用,然后调用q->make_request_fn所指函数即将该请求加入到请求队列。
对 make_request_fn 函数的调用可以认为是 IO 调度层的入口,该函数用于向请求队列中添加请求。该函数是在创建请求队列时指定的,代码如下(blk_init_queue 函数中):
函数 blk_queue_make_request 将函数 __make_request 的地址赋予了请求队列 q 的 make_request_fn 成员
__make_request 函数的主要工作为:
将请求放入到请求队列中后,何时被处理就由 IO 调度器的调度算法决定了(有关 IO 调度器的算法内容请参见参考资料)。一旦该请求能够被处理,便调用请求队列中成员 request_fn 所指向的函数处理。
request_fn 函数是块设备驱动层的入口。它是在驱动程序创建请求队列时由驱动程序传递给 IO 调度层的。IO 调度层通过回调 request_fn 函数的方式,把请求交给了驱动程序。而驱动程序从该函数的参数中获得上层发出的 IO 请求,并根据请求中指定的信息操作设备控制器(这一请求的发出需要依据物理设备指定的规范进行)。
当设备完成了 IO 请求之后,通过中断的方式通知 cpu ,而中断处理程序又会调用 request_fn 函数进行处理。当驱动再次处理该请求时,会根据本次数据传输的结果通知上层函数本次 IO 操作是否成功,如果成功,上层函数解锁 IO 操作所涉及的页面(在 do_generic_mapping_read 函数中加的锁)。该页被解锁后, do_generic_mapping_read() 函数就可以再次成功获得该锁(数据的同步点),并继续执行程序了。之后,函数 sys_read 可以返回了。最终 read 系统调用也可以返回了。
至此, read 系统调用从发出到结束的整个处理过程就全部结束了。