首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

read 系统调用剖析(3)Ext2 文件系统层的处理

read 系统调用剖析(3)Ext2 文件系统层的处理

Ext2 文件系统层的处理图4 read 系统调用在 ext2 层中处理时函数调用关系由图 4 可知,该层入口函数 generic_file_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 函数。
在讲解 do_generic_mapping_read 函数都作了哪些工作之前,我们再来看一下文件在内存中的缓存区域是被怎么组织起来的。
文件的 page cache 结构图5显示了一个文件的 page cache 结构。文件被分割为一个个以 page 大小为单元的数据块,这些数据块(页)被组织成一个多叉树(称为 radix 树)。树中所有叶子节点为一个个页帧结构(struct page),表示了用于缓存该文件的每一个页。在叶子层最左端的第一个页保存着该文件的前4096个字节(如果页的大小为4096字节),接下来的页保存着文件第二个4096个字节,依次类推。树中的所有中间节点为组织节点,指示某一地址上的数据所在的页。此树的层次可以从0层到6层,所支持的文件大小从0字节到16 T 个字节。树的根节点指针可以从和文件相关的 address_space 对象(该对象保存在和文件关联的 inode 对象中)中取得(更多关于 page cache 的结构内容请参见参考资料)。
图5 文件的 page cache 结构现在,我们来看看函数 do_generic_mapping_read 都作了哪些工作, do_generic_mapping_read 函数代码较长,本文简要介绍下它的主要流程:
  • 根据文件当前的读写位置,在 page cache 中找到缓存请求数据的 page
  • 如果该页已经最新,将请求的数据拷贝到用户空间
  • 否则, Lock 该页
  • 调用 readpage 函数向磁盘发出添页请求(当下层完成该 IO 操作时会解锁该页),代码:
1
error = mapping->a_ops->readpage(filp, page);




  • 再一次 lock 该页,操作成功时,说明数据已经在 page cache 中了,因为只有 IO 操作完成后才可能解锁该页。此处是一个同步点,用于同步数据从磁盘到内存的过程。
  • 解锁该页
  • 到此为止数据已经在 page cache 中了,再将其拷贝到用户空间中(之后 read 调用可以在用户空间返回了)
到此,我们知道:当页上的数据不是最新的时候,该函数调用 mapping->a_ops->readpage 所指向的函数(变量 mapping 为 inode 对象中的 address_space 对象),那么这个函数到底是什么呢?
Readpage 函数的由来address_space 对象是嵌入在 inode 对象之中的,那么不难想象: address_space 对象成员 a_ops 的初始化工作将会在初始化 inode 对象时进行。如清单3中后半部所显示。
1
2
3
4
if (test_opt(inode->i_sb, NOBH))
inode->i_mapping->a_ops = &ext2_nobh_aops;
else
    inode->i_mapping->a_ops = &ext2_aops;




可以知道 address_space 对象的成员 a_ops 指向变量 ext2_aops 或者变量 ext2_nobh_aops 。这两个变量的初始化如清单5所示。
清单5 变量 ext2_aops 和变量 ext2_nobh_aops 的初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct address_space_operations ext2_aops = {
    .readpage          =ext2_readpage,
    .readpages         = ext2_readpages,
    .writepage         = ext2_writepage,
    .sync_page         = block_sync_page,
    .prepare_write     = ext2_prepare_write,
    .commit_write       = generic_commit_write,
    .bmap                 = ext2_bmap,
    .direct_IO           = ext2_direct_IO,
    .writepages          = ext2_writepages,
};

struct address_space_operations ext2_nobh_aops = {
    .readpage= ext2_readpage,
    .readpages           = ext2_readpages,
    .writepage           = ext2_writepage,
    .sync_page           = block_sync_page,
    .prepare_write      = ext2_nobh_prepare_write,
    .commit_write       = nobh_commit_write,
    .bmap                 = ext2_bmap,
    .direct_IO           = ext2_direct_IO,
    .writepages          = ext2_writepages,
};




从上述代码中可以看出,不论是哪个变量,其中的 readpage 成员都指向函数 ext2_readpage 。所以可以断定:函数 do_generic_mapping_read 最终调用 ext2_readpage 函数处理读数据请求。
到此为止, ext2 文件系统层的工作结束。
返回列表