跟踪系统调用之旅-续2

简介: 函数generic_file_read()通过执行宏init_sync_kiocb来初始化描述符kiocb,并设置一个同步操作对象的有关字段。具体地说就是,改宏设置ki_key字段为KIOCB_SYNC_KEY,ki_filp字段为filp、ki_obj字段为current。

函数generic_file_read()通过执行宏init_sync_kiocb来初始化描述符kiocb,并设置一个同步操作对象的有关字段。具体地说就是,改宏设置ki_key字段为KIOCB_SYNC_KEYki_filp字段为filpki_obj字段为current

 然后将刚填好的ioveckiocb描述符地址传给__ 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函数主要流程如下:

  • 根据文件当前的读写位置,在 page cache 中找到缓存请求数据的 page
  • 如果该页已经最新,将请求的数据拷贝到用户空间
  • 否则, Lock 该页
  • 调用 readpage 函数向磁盘发出添页请求(当下层完成该 IO 操作时会解锁该页),代码:
                
                
  • 再一次 lock 该页,操作成功时,说明数据已经在 page cache 中了,因为只有 IO 操作完成后才可能解锁该页。此处是一个同步点,用于同步数据从磁盘到内存的过程。
  • 解锁该页
  • 到此为止数据已经在 page cache 中了,再将其拷贝到用户空间中(之后 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_readpageso: 函数 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 函数的主要工作为:

  1. 检测请求队列是否为空,若是,延缓驱动程序处理当前请求(其目的是想积累更多的请求,这样就有机会对相邻的请求进行合并,从而提高处理的性能),并跳到3,否则跳到2
  2. 试图将当前请求同请求队列中现有的请求合并,如果合并成功,则函数返回,否则跳到3
  3. 该请求是一个新请求,创建新的请求描述符,并初始化相应的域,并将该请求描述符加入到请求队列中,函数返回

将请求放入到请求队列中后,何时被处理就由 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 系统调用从发出到结束的整个处理过程就全部结束了。
      



目录
相关文章
|
3月前
|
存储 Linux API
Linux源码阅读笔记08-进程调度API系统调用案例分析
Linux源码阅读笔记08-进程调度API系统调用案例分析
|
4月前
|
监控 安全 Linux
Linux命令truss详解:系统调用跟踪的利器
`truss`(或`strace`)是Linux调试利器,用于跟踪系统调用和信号。它帮助开发者优化性能、调试错误和进行安全审计。通过附加到进程,记录调用细节、参数、返回值和错误。使用参数如`-d`显示调试信息,`-e`跟踪特定调用,`-o`输出到文件,`-p`跟踪指定进程。注意其对性能的影响,通常需要root权限,并建议过滤输出和结合其他工具分析。
|
6月前
|
NoSQL Java 编译器
|
6月前
|
监控 Linux Shell
Linux 进程问题调查探秘:分析和排查频繁创建进程问题
Linux 进程问题调查探秘:分析和排查频繁创建进程问题
131 0
|
6月前
|
算法 Ubuntu Linux
【操作系统】探究进程奥秘:显示进程列表的解密与实战
【操作系统】探究进程奥秘:显示进程列表的解密与实战
60 0
|
C语言
《计算机操作系统-第三章》之中断与系统调用
《计算机操作系统-第三章》之中断与系统调用
270 0
|
缓存 数据格式 流计算
xawtv涉及的vivid系统调用分析
xawtv涉及的vivid系统调用分析
197 0
|
存储 安全
【操作系统】第二章:OS的启动和中断、异常、系统调用(一)
【操作系统】第二章:OS的启动和中断、异常、系统调用
337 0
|
安全 Java Linux
【操作系统】第二章:OS的启动和中断、异常、系统调用(二)
【操作系统】第二章:OS的启动和中断、异常、系统调用
186 0
|
安全 编译器 API
【操作系统】第二章启动、中断、异常和系统调用
【操作系统】第二章启动、中断、异常和系统调用
184 0
【操作系统】第二章启动、中断、异常和系统调用