linux的数据一致性和io类型怎么理解

69次阅读
没有评论

共计 3172 个字符,预计需要花费 8 分钟才能阅读完成。

这篇文章主要讲解了“linux 的数据一致性和 io 类型怎么理解”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着丸趣 TV 小编的思路慢慢深入,一起来研究和学习“linux 的数据一致性和 io 类型怎么理解”吧!

对 linux 内核来说,读写要经过层层路径,才能真正读写硬盘。从 io 路径来说,io 要经过 page cache,io 调度队列,dispatch 队列,ncq 队列和硬盘 cache,才能真正到达硬盘。

Page cache:page cache 是 linux 内核提供的缓存接口,page cache 的名字就说明内核是通过 page 单元(通常 4K 大小)来管理 cache。读操作首先在 page cache 查找,如果找到,就复制 page cache 的内容返回,找不到,才真正调用下层处理。写操作,buffer io 写到 page cache 就返回,真正的磁盘写,是由内核的 pdflush 内核线程负责

IO 调度队列:

Linux 内核提供了四种 io 调度算法,as,deadline,cfq,noop。每种调度算法都实现了一个调度队列,io 首先在队列中排序(noop 最简单,不排序),然后根据条件,决定是否到 dispatch 队列。从调度队列下发,涉及一个 unplug 的概念。也就是说,调度队列通常处于阻塞(plug)状态,当执行 unplug 操作时,io 离开调度队列,开始下发。unplug 是个循环动作,将调度队列的所有 io 都尝试下发,直到不能下发为止。
总结一下,执行 unplug 有下列条件:

第一个 io 启动了三毫秒的定时器,定时器到了,会 unplug,开始下发

io 请求超过设定的限制(缺省是 4),执行 unplug,开始下发

Sync 标志的 io,立即 unplug,开始下发。

Barrier 标志的 io,清空调度队列后,执行 unplug,开始下发

一个 io 执行完毕,也要 unplug 队列。

dispatch 队列:dispatch 队列对应用关系不大。但是内核层对日志文件系统的 joural 数据,提供了一种 barrier io,这个主要在 dispatch 队列实现。

Ncq 队列:
NCQ 是 sata 硬盘自身的队列。(sas 硬盘的队列叫 TCQ)。NCQ 队列是由操作系统创建的,但是加入到 NCQ 队列的 io,是由硬盘来决定执行顺序。为了实现这个,NCQ 队列创建在内核的 DMA 内存中,然后通知硬盘,至于硬盘选择那个 io 执行,是硬盘自身选择的结果。

硬盘 cache:
硬盘 cache 是硬盘内部的 cache。如果打开硬盘 cache 的话,写硬盘的 io,首先是到硬盘 cache,而非直接落到硬盘。

一. Pdflush 的回写逻辑

Pdflush 提供了四个参数来控制回写。在内核实现中,pdflush 的回写策略控制还比较复杂。

但是简单一点说,内核缺省情况下,每 5 秒钟扫描脏页,如果脏页生存时间超过 30 秒(缺省数值),就刷脏页到磁盘。

详细的可参考本人写的《linux 内核回写机制和调整》一文。

二. 数据下盘和一致性分析

从上文的分析,通常的 io 写,到 page cache 层就结束返回了,并没真正写到硬盘。这样机器掉电或者故障的时候,就有丢失数据的风险。为了尽快下 io,系统又提供了一些措施解决这个问题。

O_SYNC: 打开文件的时候,可以设置 O_SYNC 标志,在 page cache 的写完成后,如果文件有 O_SYNC 标志,立即开始将 io 下发,进入调度队列。随后将文件系统的 meta data 数据也下发,然后开始循环执行 unplug 操作,直到所有的写 io 完成。和回写机制比较,O_SYNC 没有等脏页生存 30 秒,就尝试立即下发到硬盘。

O_SYNC 本质就是 io 下发,然后执行 unplug 操作。O_SYNC 的几个问题是:

写 page cache 时候要把 io 拆成 4k 的单元。回写也是每次写 4K 的页面,如果是大 io,就需要内核的调度层把 4k 的 io 重新再合并起来。这是冗余的过程

每个 io 都要立即 unplug,这样就不能实现 io 的排序和合并。O_SYNC 的性能相当低。

如果多个进程并发写,不能保证写操作的顺序。Ncq 队列根据硬盘磁头的位置和磁盘旋转位置确定执行的顺序。一般是 meta data 数据一起写,这样存在不同步的风险。

如果硬盘 cache 打开了,那么写只到硬盘 cache 就返回了。存在丢数据的风险。通常存储厂商都要求硬盘 cache 关闭。不过腾讯的服务器都是打开硬盘 cache 的。

O_DIRECT:打开文件的时候,可设置 O_DIRECT 标志。O_DIRECT 不使用内核提供的 page cache。这样读操作,就不会到 page cache 中检查是否有需要数据存在。而写操作,也不会将数据写入 page cache,而是送入调度队列。

O_DIRECT 执行写 io 的时候,会置 WRITE_SYNC 标志。这个标志在 io 进入调度队列后,会执行一次 unplug 操作。而不是像 O_SYNC 那样,循环执行 unplug 操作。

为了避免 O_SYNC 每个写 io 都要阻塞等待的问题,系统提供了 fsync 和 fdatasync 系统调用,可以让应用层自己控制同步的时机。

Fsync:fsync 将文件范围内,所有的脏页面都下发到硬盘。然后也要将脏的元数据写到 硬盘。如果文件的 inode 本身有变化,同样需要写到硬盘。

Fdatasync:fdatasync 和 fsync 的区别其实很轻微。比如 ext2 文件系统,如果文件的 inode 只有轻微的变化,fdatasync 此时不更新 inode。典型的轻微变化是文件 atime 的变化。而在 ext3 文件系统,fsync 和 fdatasync 是完全一样的。不管是否轻微变化,都要回写 inode。

Fsync 和 fdatasync 都是对整个文件的操作,如果应用只想刷新文件的指定位置,这两个系统调用就失效了。所以新的内核还提供了 sync_file_range 来指定范围写。不过要注意,sync_file_range 是不回写文件的 meta data。必须应用层保证 meta data 没有更新。

三. Pdflush 的回写逻辑

Pdflush 提供了四个参数来控制回写。在内核实现中,pdflush 的回写策略控制还比较复杂。

但是简单一点说,内核缺省情况下,每 5 秒钟扫描脏页,如果脏页生存时间超过 30 秒(缺省数值),就刷脏页到磁盘。

四. 内核的 barrier io

从上文的分析看出,内核没有为用户态提供保证顺序的,确定写到硬盘的系统调用。但是对于内核文件系统来说,必须提供这样的接口。比如日志文件系统,必须要数据落到硬盘后,才能修改元数据的日志。否则,出错情况下就可能造成文件系统崩溃。为此,内核专门提供了一个 barrier 方式实现日志的准确写到硬盘。

文件系统的 barrier io,意味着,这个 barrier io 之前的写 io 必须完成。同时,在 barrier io 完成之前(是真正写到硬盘,不是写到 cache 就返回),也不能有别的写 io 再执行。为此,上文分析的 dispatch 队列完成了这个功能。

当写 io 从调度队列进入 dispatch 队列的时候,要检查是否是一个 barrier io。如果是 barrier io,dispatch 首先在队列中插入一个 SCSI 命令 SYNCHRONIZE_CACHE,这个命令指示硬盘 cache 刷所有的写 io 到硬盘。然后再下发 barrier io,之后再插入一个 SYNCHRONIZE_CACHE 命令,指示硬盘将刚才的 barrier io 真正写到硬盘(还有一种方式,是通过命令携带 FUA 标志实现不经过 cache 直接下盘)。

感谢各位的阅读,以上就是“linux 的数据一致性和 io 类型怎么理解”的内容了,经过本文的学习后,相信大家对 linux 的数据一致性和 io 类型怎么理解这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是丸趣 TV,丸趣 TV 小编将为大家推送更多相关知识点的文章,欢迎关注!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-04发表,共计3172字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)