Linux--Sys_Read系统调用过程分析

注:

本片文章以Read函数的调用为例来讲述一下系统对块驱动层的一些处理, 哈哈。如果有不正确或者不完善的地方,欢迎前来拍砖留言或者发邮件到guopeixin@126.com进行讨论,先行谢过。

一.Read函数经由的层次模型

首先来了解一下Read函数经由的层次模型:

clip_image002

从图中可以看出,对于磁盘的一次读请求,首先经过虚拟文件系统层(vfs layer),其次是具体的文件系统层(例如 ext2),接下来是 cache 层(page cache 层)、通用块层(generic block layer)、IO 调度层(I/O scheduler layer)、块设备驱动层(block device driver layer),最后是物理块设备层(block device layer)。

下面摘抄一份文档,来对上面的各个层面的作用做一些简述:

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

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

• 引入 cache 层的目的是为了提高 linux 操作系统对磁盘访问的性能。 Cache 层在内存中缓存了磁盘上的部分数据。当数据的请求到达时,如果在 cache 中存在该数据且是最新的,则直接将数据传递给用户程序,免除了对底层磁盘的操作,提高了性能。

• 通用块层的主要工作是:接收上层发出的磁盘请求,并最终发出 IO 请求。该层隐藏了底层硬件块设备的特性,为块设备提供了一个通用的抽象视图。

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

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

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

二.系统调用的发起点sys_read

1. sys_read代码分析

Sys_read最终被注册为系统API,在很多的系统模块中都可以看到该API的调用。

函数sys_read()的代码如下:

asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count)

{

struct file *file;

ssize_t ret = -EBADF;

int fput_needed;

file = fget_light(fd, &fput_needed);

if (file) {

loff_t pos = file_pos_read(file);

ret = vfs_read(file, buf, count, &pos);

file_pos_write(file, pos);

fput_light(file, fput_needed);

}

return ret;

}

ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)

{

ssize_t ret;

if (!(file->f_mode & FMODE_READ))

return -EBADF;

if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read))

return -EINVAL;

if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))

return -EFAULT;

ret = rw_verify_area(READ, file, pos, count);

if (ret >= 0) {

count = ret;

if (file->f_op->read)

ret = file->f_op->read(file, buf, count, pos);

else

ret = do_sync_read(file, buf, count, pos);

if (ret > 0) {

fsnotify_access(file->f_path.dentry);

add_rchar(current, ret);

}

inc_syscr(current);

}

return ret;

}

从上面可以看到,调用Stack为sys_read()àvfs_read()àfile->f_op->read()。而file->f_op->read实际上就是具体的文件系统向通用Block层注册的一个函数指针,对于本文中讲述的EXT2文件系统来说,实际上就是do_sync_read。

三.Ext2文件系统在sys_read调用过程中的角色

1. Ext2文件系统file_operations接口的注册过程

Ext2文件系统的模块初始化函数会去注册操作接口ext2_file_operations,调用Stack如下init_ext2_fs()à register_filesystem()àext2_get_sb()àext2_fill_super()àext2_iget(),其中函数ext2_iget()会获取结构体file_operations的值。其中,接口的定义如下:

/*

* We have mostly NULL's here: the current defaults are ok for

* the ext2 filesystem.

*/

const struct file_operations ext2_file_operations = {

.llseek = generic_file_llseek,

.read = do_sync_read,

.write = do_sync_write,

.aio_read = generic_file_aio_read,

.aio_write = generic_file_aio_write,

.unlocked_ioctl = ext2_ioctl,

#ifdef CONFIG_COMPAT

.compat_ioctl = ext2_compat_ioctl,

#endif

.mmap = generic_file_mmap,

.open = generic_file_open,

.release = ext2_release_file,

.fsync = ext2_sync_file,

.splice_read = generic_file_splice_read,

.splice_write = generic_file_splice_write,

};

const struct address_space_operations ext2_aops = {

.readpage = ext2_readpage,

.readpages = ext2_readpages,

.writepage = ext2_writepage,

.sync_page = block_sync_page,

.write_begin = ext2_write_begin,

.write_end = generic_write_end,

.bmap = ext2_bmap,

.direct_IO = ext2_direct_IO,

.writepages = ext2_writepages,

.migratepage = buffer_migrate_page,

.is_partially_uptodate = block_is_partially_uptodate,

};

const struct address_space_operations ext2_nobh_aops = {

.readpage = ext2_readpage,

.readpages = ext2_readpages,

.writepage = ext2_nobh_writepage,

.sync_page = block_sync_page,

.write_begin = ext2_nobh_write_begin,

.write_end = nobh_write_end,

.bmap = ext2_bmap,

.direct_IO = ext2_direct_IO,

.writepages = ext2_writepages,

.migratepage = buffer_migrate_page,

};

而函数ext2_iget()中的相关代码如下:

struct inode *ext2_iget (struct super_block *sb, unsigned long ino)

{

...

if (S_ISREG(inode->i_mode)) {

inode->i_op = &ext2_file_inode_operations;

if (ext2_use_xip(inode->i_sb)) {

inode->i_mapping->a_ops = &ext2_aops_xip;

inode->i_fop = &ext2_xip_file_operations;

} else if (test_opt(inode->i_sb, NOBH)) {

inode->i_mapping->a_ops = &ext2_nobh_aops;

inode->i_fop = &ext2_file_operations;

} else {

inode->i_mapping->a_ops = &ext2_aops;

inode->i_fop = &ext2_file_operations;

}

} else if (S_ISDIR(inode->i_mode)) {

inode->i_op = &ext2_dir_inode_operations;

inode->i_fop = &ext2_dir_operations;

if (test_opt(inode->i_sb, NOBH))

inode->i_mapping->a_ops = &ext2_nobh_aops;

else

inode->i_mapping->a_ops = &ext2_aops;

} else if (S_ISLNK(inode->i_mode)) {

if (ext2_inode_is_fast_symlink(inode))

inode->i_op = &ext2_fast_symlink_inode_operations;

else {

inode->i_op = &ext2_symlink_inode_operations;

if (test_opt(inode->i_sb, NOBH))

inode->i_mapping->a_ops = &ext2_nobh_aops;

else

inode->i_mapping->a_ops = &ext2_aops;

}

} else {

inode->i_op = &ext2_special_inode_operations;

if (raw_inode->i_block[0])

init_special_inode(inode, inode->i_mode,

old_decode_dev(le32_to_cpu(raw_inode->i_block[0])));

else

init_special_inode(inode, inode->i_mode,

new_decode_dev(le32_to_cpu(raw_inode->i_block[1])));

}

...

}

2. 系统Read过程调用在该层的Stack

image

四.Page Cache在Sys_read调用过程中所做的工作

1. Page Cache在Sys_read调用过程中所做的工作

从前面粘贴的函数ext2_iget()的代码中中可以看到inode->i_mapping->a_ops = &ext2_aops,实际上这里就是注册了页面缓存的一些接口。

上一部分提到Ext2调用的结束点就是mappingàa_opsàreadpage(file, page),实际上执行的就是ext2_aops.readpage(file, page),也即ext2_readpage。

有关函数ext2_readpage()的调用Stack如下:

image

五.通用Block层和IO Schedule层扮演的角色

这部分相对比较简单,通过函数submit_bio()的调用直接可以找到,相关调用Stack如下:

image

六.Driver所做的事情

哎呀,分析了半天还没有看到块设备驱动的参与,不要急,这里就来了,呵呵。

在块设备驱动中一般会调用通过Block层的导出函数blk_init_queue()来注册执行具体操作的函数,形如q->request_fn = rfn。

相关代码如下:

struct request_queue *

blk_init_queue_node(request_fn_proc *rfn, spinlock_t *lock, int node_id)

{

struct request_queue *q = blk_alloc_queue_node(GFP_KERNEL, node_id);

if (!q)

return NULL;

q->node = node_id;

if (blk_init_free_list(q)) {

kmem_cache_free(blk_requestq_cachep, q);

return NULL;

}

/*

* if caller didn't supply a lock, they get per-queue locking with

* our embedded lock

*/

if (!lock)

lock = &q->__queue_lock;

q->request_fn = rfn;

q->prep_rq_fn = NULL;

q->unplug_fn = generic_unplug_device;

q->queue_flags = (1 << QUEUE_FLAG_CLUSTER);

q->queue_lock = lock;

blk_queue_segment_boundary(q, 0xffffffff);

blk_queue_make_request(q, __make_request);

blk_queue_max_segment_size(q, MAX_SEGMENT_SIZE);

blk_queue_max_hw_segments(q, MAX_HW_SEGMENTS);

blk_queue_max_phys_segments(q, MAX_PHYS_SEGMENTS);

q->sg_reserved_size = INT_MAX;

blk_set_cmd_filter_defaults(&q->cmd_filter);

/*

* all done

*/

if (!elevator_init(q, NULL)) {

blk_queue_congestion_threshold(q);

return q;

}

blk_put_queue(q);

return NULL;

}

至此,整个流程分析完毕。

最终,汇总的流程图如下:

image

注:

根据吴仲杰大哥描述,函数request_fn确实是块驱动的入口。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/243121.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

负债的阶梯,你在第几层?

现在的年轻人成为了消费市场上的中流砥柱&#xff0c;他们为我国的GDP贡献了70%的数据。如今年轻人更愿意刷信用卡、网贷平台借钱消费。尽管在外打拼不容易&#xff0c;但是工作稳定&#xff0c;收入稳定&#xff0c;为了犒劳自己及时享乐&#xff0c;他们选择了背负一点债务。…

Android应用程序开发环境的建立

注&#xff1a; 本片文章简单的描述一下Android应用程序开发环境的建立。如果有不正确或者不完善的地方&#xff0c;欢迎前来拍砖留言或者发邮件到guopeixin126.com进行讨论&#xff0c;先行谢过。 一&#xff0e;准备工作... 2 二&#xff0e;安装步骤... 2 1. 安装JDK5.0.…

你真的会 snapshot 吗? - 每天5分钟玩转 OpenStack(163)

​这是 OpenStack 实施经验分享系列的第 13 篇。 instance snapshot 操作可用于备份或者将 instance 保存为新的 image。如果在生产系统中执行 snapshot 操作&#xff0c;必须确保此操作快速且安全。这里有两个关键点&#xff1a; 快速。 为保证数据的一致性&#xff0c;snaps…

输入序列连续的序列检测

输入序列连续的序列检测 题目描述 请编写一个序列检测模块&#xff0c;检测输入信号a是否满足01110001序列&#xff0c;当信号满足该序列&#xff0c;给出指示信号match。 模块的接口信号图如下&#xff1a; 模块的时序图如下&#xff1a; timescale 1ns/1ns module seque…

哪些信用卡取现0手续费?

经常使用信用卡的朋友都知道&#xff0c;信用卡取现一般是要手续费的&#xff0c;而且取现之后不能享受免息期&#xff0c;这点让人很不爽。有时候我们经常会急用钱&#xff0c;这时候用信用卡取现确实很方便&#xff0c;可以解决燃眉之急&#xff0c;能帮上不少忙&#xff0c;…

如何申请到利息低的贷款?

经常有朋友问我&#xff0c;如何才能申请到利息低的贷款&#xff1f;针对这个问题&#xff0c;很多时候我不能给予一个明确的回答。很多朋友之前可能都没有接触过贷款&#xff0c;所以对贷款本身并不是很了解&#xff0c;更有的朋友以为贷款利息可以像商品一样讨价还价&#xf…

含有无关项的序列检测

含有无关项的序列检测 题目描述 请编写一个序列检测模块&#xff0c;检测输入信号a是否满足011XXX110序列&#xff08;长度为9位数据&#xff0c;前三位是011&#xff0c;后三位是110&#xff0c;中间三位不做要求&#xff09;&#xff0c;当信号满足该序列&#xff0c;给出指…

Metadata Service 最高频的应用 - 每天5分钟玩转 OpenStack(164)

实现 instance 定制化&#xff0c;cloud-init&#xff08;或 cloudbase-init&#xff09;只是故事的一半&#xff0c;metadata service 则是故事的的另一半。两者的分工是&#xff1a;metadata service 为 cloud-init 提供自定义配置数据&#xff0c;cloud-init 完成配置工作。…

如何获取并分析Bluetooth HCI层Command Packet和Event Packet包

首先&#xff0c;如何来确定什么样的包为Command/Event Packet呢&#xff1f; 我们知道&#xff0c;通过HCI Packet包括四种&#xff0c;即Command&#xff0c;Event&#xff0c;ACL和SCO/eSCO&#xff0c;对应到MS-Stack中的定义&#xff0c;即为COMMAND_PACKET &#xff0c;E…

Metadata Service 架构详解 - 每天5分钟玩转 OpenStack(165)

下面是 Metadata Service 的架构图&#xff0c;本节我们详细讨论各个组件以及它们之间的关系。 nova-api-metadata nova-api-metadata 是 nova-api 的一个子服务&#xff0c;它是 metadata 的提供者&#xff0c;instance 可以通过 nova-api-metadata 的 REST API 来获取 metada…

hi3531交叉编译环境arm-hisiv100nptl-linux搭建过程

安装SDK 1、Hi3531 SDK包位置 在"Hi3531_V100R001***/01.software/board"目录下&#xff0c;您可以看到一个 Hi3531_SDK_Vx.x.x.x.tgz 的文件&#xff0c; 该文件就是Hi3531的软件开发包。 2、解压缩SDK包 在linux服务器上&#xff08;或者一台装有linux的PC上&…

不重叠序列检测

不重叠序列检测 题目描述 请编写一个序列检测模块&#xff0c;检测输入信号&#xff08;a&#xff09;是否满足011100序列&#xff0c; 要求以每六个输入为一组&#xff0c;不检测重复序列&#xff0c;例如第一位数据不符合&#xff0c;则不考虑后五位。一直到第七位数据即下一…

首付贷换了马甲,又重现江湖了

据《每日经济新闻》报道&#xff0c;广州某小贷公司通过无抵押信用贷款&#xff0c;一个月入万元左右的普通市民可以获得30万元贷款&#xff0c;高者可获得50万元无抵押信用贷款用于垫付首付款。首付贷以“消费贷”的名义&#xff0c;换了马甲又重现江湖了。根据《每日经济新闻…

获取 metadata 的完整例子 - 每天5分钟玩转 OpenStack(166)

我们将通过实验详细分析 instance 从 nova-api-metadata 获取信息的完整过程。 环境介绍 1. 一个 all-in-one 环境&#xff08;多节点类似&#xff09;。 2. 已创建 neutron 网络 test_net&#xff0c;DHCP 已启动。在这个 metadata 实验中&#xff0c; test_net 的 type 不重…

输入序列不连续的序列检测

输入序列不连续的序列检测 题目描述 请编写一个序列检测模块&#xff0c;输入信号端口为data&#xff0c;表示数据有效的指示信号端口为data_valid。当data_valid信号为高时&#xff0c;表示此刻的输入信号data有效&#xff0c;参与序列检测&#xff1b;当data_valid为低时&am…

这些贷款合同陷阱你知道多少?

贷款合同可以是正式的贷款协议书&#xff0c;可以包括收条&#xff0c;也可以是个人借条。无论是哪一种方式&#xff0c;都有可能存在陷阱。我们先来看一个案例&#xff1a;王先生去找一家贷款机构办理贷款&#xff0c;因为王先生的资质比较好&#xff0c;很多贷款机构都希望能…

一个人到底申请几张信用卡最合适?

一个到底有几张信用卡最合适呢&#xff1f;这个问题估计很多朋友都没有好好的想过&#xff0c;以至于信用卡办办办&#xff0c;结果信用卡一大推&#xff0c;一不小心就走上了不归路。我们先来看下一个网友的真实情况&#xff1a;“每个月信用卡利息都要还好多&#xff0c;工资…

Fedora20配置tftp服务器

http://bbs.52dtv.com/thread-25392-1-1.html TFTP&#xff08;Trivial File Transfer Protocol&#xff0c;简单文件传输协议&#xff09;是TCP/IP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议&#xff0c;提供不复杂、开销不大的文件传输服务。端口号为69…

数据串转并电路

数据串转并电路 题目描述 实现串并转换电路&#xff0c;输入端输入单bit数据&#xff0c;每当本模块接收到6个输入数据后&#xff0c;输出端输出拼接后的6bit数据。本模块输入端与上游的采用valid-ready双向握手机制&#xff0c;输出端与下游采用valid-only握手机制。数据拼接…

获取 metadata 过程详解 - 每天5分钟玩转 OpenStack(167)

接上节&#xff0c;启动 neutron router 后 instance c1 终于拿到了 metadata, 从下面 c1 的启动日志可知&#xff1a; c1 所认为的 metadata 服务地址是 169.254.169.254&#xff0c;端口为 80。我们在 c1 中尝试访问一下 metadata。 确实能够拿到 metadata。但我们知道 nova-…