read系统调用流程

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Read 系统调用在用户空间中的处理过程

Linux 系统调用(SCI,system call interface)的实现机制实际上是一个多路汇聚以及分解的过程,该汇聚点就是 0x80 中断这个入口点(X86 系统结构)。也就是说,所有系统调用都从用户空间中汇聚到 0x80 中断点,同时保存具体的系统调用号。当 0x80 中断处理程序运行时,将根据系统调用号对不同的系统调用分别处理(调用不同的内核函数处理)。系统调用的更多内容,请参见参考资料。

Read 系统调用也不例外,当调用发生时,库函数在保存 read 系统调用号以及参数后,陷入 0x80 中断。这时库函数工作结束。Read 系统调用在用户空间中的处理也就完成了。

回页首

Read 系统调用在核心空间中的处理过程

0x80 中断处理程序接管执行后,先检察其系统调用号,然后根据系统调用号查找系统调用表,并从系统调用表中得到处理 read 系统调用的内核函数

sys_read ,最后传递参数并运行 sys_read 函数。至此,内核真正开始处理 read 系统调用(sys_read 是 read 系统调用的内核入口)。

在讲解 read 系统调用在核心空间中的处理部分中,首先介绍了内核处理磁盘请求的层次模型,然后再按该层次模型从上到下的顺序依次介绍磁盘读请求在各层的处理过程。

Read 系统调用在核心空间中处理的层次模型

图1显示了 read 系统调用在核心空间中所要经历的层次模型。从图中看出:对于磁盘的一次读请求,首先经过虚拟文件系统层(vfs layer),其次是具体的文件系统层(例如 ext2),接下来是 cache 层(page cache 层)、通用块层(generic block layer)、IO 调度层(I/O scheduler layer)、块设备驱动层(block device driver layer),最后是物理块设备层(block device layer)

图1 read 系统调用在核心空间中的处理层次

∙虚拟文件系统层的作用:屏蔽下层具体文件系统操作的差异,为上层的操作提供一个统一的接口。正是因为有了这个层次,所以可以把设备抽象成文件,使得操作设备就像操作文件一样简单。

∙在具体的文件系统层中,不同的文件系统(例如 ext2 和 NTFS)具体的操作过程也是不同的。每种文件系统定义了自己的操作集合。关于文件系统的更多内容,请参见参考资料。

∙引入 cache 层的目的是为了提高 linux 操作系统对磁盘访问的性能。

Cache 层在内存中缓存了磁盘上的部分数据。当数据的请求到达时,如果在 cache 中存在该数据且是最新的,则直接将数据传递给用户程序,免

除了对底层磁盘的操作,提高了性能。

∙通用块层的主要工作是:接收上层发出的磁盘请求,并最终发出 IO 请求。

该层隐藏了底层硬件块设备的特性,为块设备提供了一个通用的抽象视

图。

∙IO 调度层的功能:接收通用块层发出的 IO 请求,缓存请求并试图合并相邻的请求(如果这两个请求的数据在磁盘上是相邻的)。并根据设置好的调度算法,回调驱动层提供的请求处理函数,以处理具体的 IO 请求。

∙驱动层中的驱动程序对应具体的物理块设备。它从上层中取出 IO 请求,并根据该 IO 请求中指定的信息,通过向具体块设备的设备控制器发送命令的方式,来操纵设备传输数据。

∙设备层中都是具体的物理设备。定义了操作具体设备的规范。

相关的内核数据结构:

∙Dentry :联系了文件名和文件的 i 节点

∙inode :文件 i 节点,保存文件标识、权限和内容等信息

∙file :保存文件的相关信息和各种操作文件的函数指针集合

∙file_operations :操作文件的函数接口集合

∙address_space :描述文件的 page cache 结构以及相关信息,并包含有操作 page cache 的函数指针集合

∙address_space_operations :操作 page cache 的函数接口集合

∙bio : IO 请求的描述

数据结构之间的关系:

图2示意性地展示了上述各个数据结构(除了 bio)之间的关系。可以看出:由dentry 对象可以找到 inode 对象,从 inode 对象中可以取出 address_space 对象,再由 address_space 对象找到 address_space_operations 对象。

File 对象可以根据当前进程描述符中提供的信息取得,进而可以找到 dentry 对象、 address_space 对象和 file_operations 对象。

图2 数据结构关系图:

前提条件:

对于具体的一次 read 调用,内核中可能遇到的处理情况很多。这里举例其中的一种情况:

∙要读取的文件已经存在

∙文件经过 page cache

∙要读的是普通文件

∙磁盘上文件系统为 ext2 文件系统,有关 ext2 文件系统的相关内容,参见参考资料

准备:

注:所有清单中代码均来自 linux2.6.11 内核原代码

读数据之前,必须先打开文件。处理 open 系统调用的内核函数为 sys_open 。所以我们先来看一下该函数都作了哪些事。清单1显示了 sys_open 的代码(省略了部分内容,以后的程序清单同样方式处理)

清单1 sys_open 函数代码

asmlinkage long sys_open(const char __user * filename, int flags, int mode)

{

……

fd = get_unused_fd();

if (fd >= 0) {

struct file *f = filp_open(tmp, flags, mode);

fd_install(fd, f);

}

……

return fd;

……

}

代码解释:

∙get_unuesed_fd() :取回一个未被使用的文件描述符(每次都会选取最小的未被使用的文件描述符)。

∙filp_open() :调用 open_namei() 函数取出和该文件相关的 dentry 和inode (因为前提指明了文件已经存在,所以 dentry 和 inode 能够查

找到,不用创建),然后调用 dentry_open() 函数创建新的 file 对象,并用 dentry 和 inode 中的信息初始化 file 对象(文件当前的读写位

置在 file 对象中保存)。注意到 dentry_open() 中有一条语句:

f->f_op = fops_get(inode->i_fop);

相关文档
最新文档