从文件read/write一个字节的过程和所发生的磁盘IO

从以下介绍可看出,“太阳底下无新鲜事”,很多以为很复杂的业务层系统的实现原理实际上在OS内核中都有原型。因此,底层原理领会了,就能游刃有余应万变需求!!!

 

IO时(不管是磁盘IO还是网络IO)的过程整体上看有两个操作(write过程与read过程相反):

1 将数据从外设读入内核态内存,如从网卡读入到内存Ring Buffer。此过程为DMA read,不需要CPU参与,完成后通过中断通知CPU。我们通常说IO操作耗时,就是这步耗时。

2 从内核态内存复制到用户态内存(通常就是应用程序进程的内存)。此过程为CPU read,需要CPU参与。之所以要有这一步而不是将外设数据直接复制到用户态内存,是因为OS出于安全考虑不允许用户态应用程序直接访问IO设备。

但深入追究的话该过程相当复杂,概要图如下:

 

 

详见下文。

 

读过程概要:

从上面两图可总结 用户层(系统调用)、内核层(VFS、PageCache、FS)、块层(通用块管理、IO调度器、磁盘驱动)、硬件层(磁盘缓存、磁盘) 四层,分别有1、3、3、2个过程

 

note:

OS支持多种文件系统,一个磁盘上可以有多个分区,每个分区可以格式化成一种文件系统,不同分区格式化成的文件系统可以不一致;OS提供了虚拟文件系统(VFS)以屏蔽对不同文件系统的访问,页缓存(PageCache)位于VFS和实际的FS之间。

磁盘自身数据读取以扇区(通常为512B)为单位、文件系统以块block(通常为4KB)为单位管理数据、PageCache以页Page为单位(通常为4KB)管理数据。可见:

一次磁盘IO的数据有多少?虽然用户只需要一个字节,但实际上至少一页(即8扇区)的数据会被从磁盘读取到内存PageCache(即使只需要一个字节);

是否一定会有磁盘IO?PageCache在内存中暂存这些Page,若下一次访问时PageCache中存在需要的数据则直接从内存取而不需要读磁盘,因此,由于PageCache的存在,读取文件一个字节并不一定会导致磁盘IO。

从上述过程看,若写数据时写入到了PageCache,则此时用户的写磁盘操作从效果上看是异步的。

详情参阅文章 read 文件一个字节实际会发生多大的磁盘IO

 

写过程概要:

 

note:

写的过程与读类似,先后经历 用户态写、系统调用写、VFS系统写、写PageCache、实际FS写、磁盘驱动写 等过程。

有多种写的方法,如同步写、异步写等,区别在于写方法是否等数据真正写入磁盘才返回。对于异步写,数据写入PageCache(此时该PageCache称为脏页)写方法就立即返回。

脏页数据由内核Worker线程周期性地扫描,将符合条件(脏页数据的 比例达到阈值、总量达到阈值、存在时间达到阈值 至少一个,阈值可配置)的脏页数据持久化到磁盘,这才是真正的“写”。持久化的时机:

数据会在如下三个时机下被真正发起写磁盘IO请求:
第一种情况,如果write系统调用时,如果发现PageCache中脏页占比太多,超过了dirty_ratio或dirty_bytes,write就必须等待了。
第二种情况,write写到PageCache就已经返回了。worker内核线程异步运行的时候,再次判断脏页占比,如果超过了dirty_background_ratio或dirty_background_bytes,也发起写回请求。
第三种情况,这时同样write调用已经返回了。worker内核线程异步运行的时候,虽然系统内脏页一直没有超过dirty_background_ratio或dirty_background_bytes,但是脏页在内存中呆的时间超过dirty_expire_centisecs了,也会发起会写
View Code

由于PageCache位于内存,而异步写时会优先考虑往PageCache写,故异步写的效率很高;但也由于PageCache在内存发生断电等异常情况时数据可能还没来得及持久化,故数据可能丢失。

详情参阅文章 write 文件一个字节实际会发生多大的磁盘IO

 

 

拓展:

从上述读写过程可知,VFS是内核中的一层重要抽象,用于屏蔽不同FS的差异,从而让OS支持各种FS。

在Linux Kernel中,有一种特殊的FS,叫做 FUSE(File system in UserSpace)

 Linux Kernel Doc :

A filesystem in which data and metadata are provided by an ordinary userspace process. The filesystem can be accessed normally through the kernel interface.

FUSE is a userspace filesystem framework. It consists of a kernel module (fuse.ko), a userspace library (libfuse.*) and a mount utility (fusermount). 

对FUSE的介绍及基于FUSE的demo,可参阅公众号文章 奇伢云存储-FUSE

简而言之:FUSE是一个特殊的文件系统,其没有文件系统的具体实现,而是一个框架,借助该框架可以在用户态提供文件系统的具体实现(fuse为用户态文件系统提供了与VFS交互的通道)。

从数据流向看,就是把用户态到内核态的读写请求转发回【用户态上用户自己实现的文件系统(假设为A)】,即 用户态 -> 内核态 -> VFS -> FUSE -> A,返回时原路返回。

从框架实现上看,其定义了一套数据协议和协议的封装/解析库,该协议就是 用户态和内核态间、内核态和A 间的数据交互协议。具体而言(其实很简单),框架包含三个模块:

内核模块 fuse.ko :用来接收 vfs 传递下来的 IO 请求,并且把这个 IO 封装之后通过管道发送到用户态;

用户态 lib 库 libfuse :解析内核态转发出来的协议包,拆解成常规的 IO 请求;

mount 工具 fusermount :用于挂载FUSE文件系统的实现,即A。

基于FUSE实现的FS有:cepthfs、glusterfs、sshfs、nfs(网络文件系统)等。

 

 

 

 

posted @ 2021-08-19 21:23  March On  阅读(1795)  评论(0编辑  收藏  举报
top last
Welcome user from
(since 2020.6.1)