透析磁盘io

概要
在这里插入图片描述

由上图可以看到,磁盘的访问速度远远小于DRAM的速度,即便是SSD依然跟DRAM有着不小的差距。在我们的程序中,程序运行的效率往往是由运行速度垫底的磁盘决定。

1,文件系统

在了解磁盘io之前,必须了解一下文件系统。这里用最简单的MINIX文件系统,以及Linux0.01版本的源码来讲解。MINIX文件系统分为6个部分,引导块(忽略),超级块,i节点,数据区,i节点位图,逻辑块位图。如下图所示。
在这里插入图片描述

整个磁盘被划分成若干个大小相同的块,每个块大小为1KB(2个扇区)。其中超级块用于放盘设备上文件系统的信息,说明各个部分的大小,比如:i节点总数,逻辑块总数,i节点位图和逻辑块位图所占用的磁盘块数。在内核中超级块使用super_block结构体来表示
在这里插入图片描述

逻辑块位图用于描述数据盘块的使用情况,位图中每一位表示数据区的一个逻辑块, 当数据盘块被占用时,在逻辑块位图中对应的位被置位1。从super_block结构体中可以看到逻辑块位图最多使用8个缓冲块(每块1Kb)。那一共就是81024kb = 881024位。每一位表示一个1kb的盘块,一共可以表示8810241kb大小的数据区,也即64MB的内存。所以Linux0.01只支持64MB的硬盘。
i节点用来存储文件的元数据,比如文件长度,修改时间,文件类型和属性,以及文件存在哪些逻辑块上。
在这里插入图片描述

其中i_zone数据指向逻辑块区,其中0-6指向直接的盘块,7为一级间接块,8为二级间接块。如果文件小于7k,那么使用0-6就可以指向7k的数据块。如果大于7k,则需要使用间接块,i_zone[7]指向附加的盘块号,MINIX文件系统中它可以放512个盘块,所以可以指向512kb的数据,如果文件大于512kb+7kb则需要使用二级间接盘块。其结构如下图所示。
在这里插入图片描述

2,磁盘写流程

磁盘写速度是非常慢的相对cpu,内存来说,那如果程序频繁写时,那么磁盘肯定是处理不过来的。怎么解决这个问题呢,缓存+队列,首先在文件写时,先将数据写入内核高速缓存。将所以的磁盘访问请求放置在请求队列中,排队访问磁盘。具体流程可参考linux0.01源码,该版本的源码足够简单,便于理解。
在这里插入图片描述

对硬盘设备上数据的读写操作时通过中断处理程序进行的。内核每次读写数据以一个逻辑块1kb为单位,而磁盘控制器是以扇区512byte为单位。在处理过程中,使用了读写请求项等待队列来顺序缓冲一次读写多个逻辑块的操作。
当然内核也提供跳过内核高速缓存的方法,那便是使用O_DIRECT标识,使用直接内存,我们常用的mysql中就有这个设计,因为mysql的innodb引擎在用户层维护了一个buffer pool,已经缓存了一份数据,所以使用O_DIRECT直接内存来将数据写入io调度层。下图为在调用sys_write时,内核的处理过程。
在这里插入图片描述

当mysql innodb在将数据写入内核时,内核并不保证将数据持久化,所以mysql提供了redolog,日志先行技术,在写入数据之前,先写一条log,就算由于服务器宕机丢失了数据,也可以通过redolog找回来,这就是mysql存储引擎被人青睐的原因。下图为mysql官网innoDb的结构图:
在这里插入图片描述

引入了redolog来恢复数据,那么如何保证redolog不丢失呢,这就要谈到mysql的事务参数 innodb_flush_log_at_trx_commit,该参数默认为1,目前我们生产用的也是这个配置1,使用1,在写入redolog时,会调用fsync系统调用,该系统调用的作用就是保证数据落盘。以下为fsync调用的流程:
在这里插入图片描述

在mysql中保证redolog落盘的源码如下图:
在这里插入图片描述
在这里插入图片描述

就是调用了sys_fsync这个系统调用。保证数据落盘。这个方法在redis的rdb数据文件保存中也可以看到。
在这里插入图片描述

上面的注释写的更清楚,确保数据不会停留在操作系统的缓存中。确保数据落入磁盘,持久化数据。

3,磁盘读流程

当程序需要读取硬盘上的一个逻辑块时,就会向缓冲区管理程序提出申请,而程序则进入睡眠等待状态。缓冲区管理程序首先在缓冲区中寻找以前是否已经读取过这块数据。如果缓冲区已有数据则直接返回,如果不存在,则产生缺页异常,将磁盘数据读入页高速缓存中再返回至用户层。这个很好理解。在此不做更多的介绍。
接下来介绍一下java fileChanneImpl transferTo方法,在很多技术文档中,这个方法被叫做数据零拷贝,接下来,我们来分析下何为数据零拷贝。Java中实现
在这里插入图片描述

以下是jni方法transferTo的实现

在这里插入图片描述

通过源码可以看到该方法是调用了系统调用sendfile,为啥sendfile会叫数据零拷贝,通过面两张图就可以看出省去的这次拷贝是在哪里省去的。
在这里插入图片描述
在这里插入图片描述

由上述两图可以看出,在将本地磁盘文件通过网卡发出去的时候,如果不使用数据零拷贝,则数据需要从内核层拷贝至用户层,如果使用了数据零拷贝,则只需要将数据读入内核高速缓存,然后直接将page页通过socket发送至客户端程序,或者是写入磁盘另一个文件。以下为sendfile的源码流程*(linux2.6),
在这里插入图片描述

Sendfile的目标fd既可以代表一个磁盘文件,也可以是一个socket。

4,总结

深入理解内核读写磁盘的过程之后,学习相关技术栈将会变得更简单。

参考文献:
zorrozou.github.io/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值