Linux完整的IO栈结构如下图,较为复杂

Linux完整的IO栈结构如下图,较为复杂

简化过后的IO栈如下图:

  1. 用户空间层:除了用户自己的应用程序外,也隐含了所有的库,例如常见的C库。我们常用的IO函数,例如open()/read()/write()是系统调用,由内核直接提供功能实现,而fopen()/fread()/fwrite()则是C库实现的函数,通过封装系统调用实现更高级的功能。
  2. 虚拟文件系统层:屏蔽具体文件系统的差异,向用户空间提供统一的入口。具体的文件系统通过register_filesystem()向虚拟文件系统注册挂载钩子,在用户挂载具体的文件系统时,通过回调挂载钩子实现文件系统的初始化。虚拟文件系统提供了inode来记录文件的元数据,dentry记录了目录项。对用户空间,虚拟文件系统注册了系统调用,例如SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)注册了open()的系统调用。
  3. 具体的文件系统层:文件系统要实现存储空间的管理,换句话说,其规划了哪些空间存储了哪些文件的数据,就像一个个收纳盒,A文件保存在这个块,B文件则放在哪个块。不同的管理策略以及其提供的不同功能,造就了各式各样的文件系统。除了类似于vfat、ext4、btrfs等常见的块设备文件系统之外,还有sysfs、procfs、pstorefs、tempfs等构建在内存上的文件系统,也有yaffs,ubifs等构建在Flash上的文件系统。
  4. 页缓存层:可以简单理解为一片存储着磁盘数据的内存,不过其内部是以页为管理单元,常见的页大小是4K。这片内存的大小不是固定的,每有一笔新的数据,则申请一个新的内存页。由于内存的性能远大于磁盘,为了提高IO性能,我们就可以把IO数据缓存在内存,这样就可以在内存中获取要的数据,不需要经过磁盘读写的漫长的等待。申请内存来缓存数据简单,如何管理所有的页缓存以及如何及时回收缓存页才是精髓。
  5. 通用块层:通用块层也可以细分为bio层和request层。页缓存以页为管理单位,而bio则记录了磁盘块与页之间的关系,一个磁盘块可以关联到多个不同的内存页中,通过submit_bio()提交bio到request层。一个request可以理解为多个bio的集合,把多个地址连续的bio合并成一个request。多个request经过IO调度算法的合并和排序,有序地往下层提交IO请求。
  6. 设备驱动与块设备层:不同块设备有不同的使用协议,而特定的设备驱动则是实现了特定设备需要的协议以正常驱使设备。对块设备而言,块设备驱动需要把request解析成一个个设备操作指令,在协议的规范下与块设备通信来交换数据。
  7. 发起一次IO读请求的过程是怎么样的呢?

用户空间通过虚拟文件系统提供的统一的IO系统调用,从用户态切到内核态。虚拟文件系统通过调用具体文件系统注册的回调,把需求传递到 具体的文件系统中。紧接着 具体的文件系统根据自己的管理逻辑,换算到具体的磁盘块地址,从页缓存寻找块设备的缓存数据。读操作一般是同步的,如果在 页缓存没有缓存数据,则向通用块层发起一次磁盘读。 通用块层合并和排序所有进程产生的的IO请求,经过 设备驱动从 块设备读取真正的数据。最后是逐层返回。读取的数据既拷贝到用户空间的buffer中,也会在页缓存中保留一份副本,以便下次快速访问。

如果 页缓存没命中,同步读会一路通到 块设备,而对于 异步写,则是把数据放到 页缓存后返回,由内核回刷进程在合适时候回刷到块设备。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值