【银河麒麟高级服务器操作系统】有关dd及cp测试差异的现象分析详解

了解更多银河麒麟操作系统全新产品,请点击访问

麒麟软件产品专区:https://product.kylinos.cn

开发者专区:https://developer.kylinos.cn

文档中心:https://documentkylinos.cn


dd现象

使用银河麒麟高级服务器操作系统执行两次dd用例后,表现不一致:

命令用例:/usr/bin/time dd if=/dev/zero of=/home/d.bin bs=2M count=4096

结果如图:

可以发现,在第二次执行dd时,速率只有412MB/S

现象猜测分析

基于上述的现象,猜测第一次dd没有等待数据完全落盘就结束了;而第二次dd是等待数据落盘之后才结束。

可以做如下复现和测试:

系统版本:

  1. Kernel:
  2. 4.19.90-89.11.v2401.ky10.x86_64
  3. Build:
  4. Kylin Linux Advanced Server
  5. release V10 SP3 2403/(Halberd)-x86_64-Build20/20240426

测试步骤:

执行:

  1. /usr/bin/time dd if=/dev/zero of=/home/d.bin bs=2M count=4096

再次执行:

  1. /usr/bin/time dd if=/dev/zero of=/home/d.bin bs=2M count=4096

可复现这个结果:

图1

分析和实验

删除d.bin后rm -rf d.bin,执行:

  1. strace -ttt -T -o strace_output_0.txt dd if=/dev/zero of=/home/d.bin bs=2M count=4096
  2. strace -ttt -T -o strace_output_1.txt dd if=/dev/zero of=/home/d.bin bs=2M count=4096

将生成两次对dd命令的系统调用报告,分析这两个output报告文件,对比close操作时,可以发现,第一次的dd,close耗时短,第二次的dd,close耗时更长

图2 strace系统调用对比

左边是第一次,close操作用不到1秒;

右边是第二次,close操作用了6秒,对比图1结果,第二次与第一次时间差接近6秒。

测试说明这个close()系统调用操作是导致第二次dd慢的原因。问题要从close调用的方向去分析。

根因分析

dd if=/dev/zero of=4G bs=1M count=4096在执行时,会以O_WRONLY|O_CREAT|O_TRUNC的方式打开文件,首次执行时,文件不存在则创建;

第二次执行时O_TRUNC就会发挥作用了,它会先把文件长度给TRUNC到0,然后再写数据;(这是dd程序本身执行的逻辑决定的)

而xfs文件系统,为了数据完整性,对这个这种情况做了加强------即:再检测到这种情况下,会在文件close时触发数据下刷,而且是同步的,就是导致dd性能降低的原因。

  1. 第一次运行时,由于目标文件/home/d.bin不存在,属于纯粹的内存操作,没有实际的磁盘IO开销。
  2. 第二次运行时,由于目标文件已存在且被写入了数据,而需要/dev/zero真实读取数据,并将数据写入磁盘文件。这就引入了额外的磁盘IO操作,成为了新的性能瓶颈。
  3. cpu占用率的差异就是由于落盘操作造成的,第一次dd时,都是操作的内存所以cpu占用率高;
  4. 第二次dd由于数据落盘会有IO等待,cpu占用率就会降下来。

验证猜测

使用watch监测页内存回写到硬盘

使用两个终端,并进行测试与观测,第一次dd

|---------------------------------------------------------------------------------------------------------------|---------------------------------|
| 1. rm -rf d.bin 2. echo 3 > /proc/sys/vm/drop_caches 3. time dd if=/dev/zero of=/home/d.bin bs=2M count=4096 | 1. watch -n 1 cat /proc/meminfo |

图三 第一次dd 的数据回写观测

第二次dd

|-----------------------------------------------------------------------------------------------|------------------------------------|
| 1. echo 3 > /proc/sys/vm/drop_caches 2. time dd if=/dev/zero of=/home/d.bin bs=2M count=4096 | 1. watch -n 1 cat /proc/meminfo 2. |

图4 第二次dd的数据回写观测

可以发现,第一次dd并没有writeback回写,即:没有从页内存往硬盘落数据,通过ls看到的d.bin的文件数据,是在内存中,只是因为有这个"机制"存在,让用户感觉这个数据落盘了;

第二次dd,由于出现了脏页(因为d.bin存在导致缓存数据不同),内核进行了数据落盘的操作,通过watch命令,观测到了在dd过程中,writeback就有数据流量。

使用sar观测I/O

|---------------------------------------------------------|-------------|
| 1. time dd if=/dev/zero of=/home/d.bin bs=2M count=4096 | 1. sar -d 1 |

第一次dd的情况

图5 第一次dd的I/O情况

第二次dd的情况

图6 第二次dd的I/O情况

可以看到,第二次的磁盘I/O操作比第一次dd多很多,说明有很多I/O落盘的操作,佐证页缓存机制在工作。

如何让dd观测结果稳定?

优化dd测试方法

既然dd的运行逻辑决定了最后观测的结果,那么可以通过指定一些参数,来更准确 的进行测试:

通过添加ofag=direct参数,不走系统缓存

测试命令和观测结果如下:

  1. root@localhost home\]# rm -rf d.bin

  2. 记录了4096+0 的读入
  3. 记录了4096+0 的写出
  4. 8589934592字节(8.6 GB,8.0 GiB)已复制,11.2041 s,767 MB/s
  5. real0m11.205s
  6. user0m0.003s
  7. sys0m0.694s
  8. root@localhost home\]# time dd if=/dev/zero of=/home/d.bin bs=2M count=4096 oflag=direct

  9. 记录了4096+0 的写出
  10. 8589934592字节(8.6 GB,8.0 GiB)已复制,11.4382 s,751 MB/s
  11. real0m11.443s
  12. user0m0.000s
  13. sys0m0.700s
  14. root@localhost home\]#

可以看到,在添加oflag=direct参数后,dd命令不管是第几次都稳定了;

通过观察页内存,可以发现,直接写时,数据落盘,没有通过脏页内存回写。

通过添加conv=notrunc方式,不截断输出文件

默认情况下,如果输出文件已经存在,dd命令会首先将其截断为0字节大小。然后再将输入数据写入该输出文件。

当指定conv=notrunc参数时,dd命令将保留输出文件已有的数据,而只是从输出文件的当前文件结尾处开始覆盖写入数据。

删除生成的d.bin文件

如果每次测试前,都删除d.bin文件,那么每次dd都是通过内存出来,速率也可以稳定

为什么FreeBSD的表现和Linux不同?

freebsd上使用zfs文件系统,而linux上使用了xfs文件系统,他们在数据下刷机制上也存在一些差异:

对于 XFS,代码中的 filemap_flush(VFS_I(ip)->i_mapping) 实际上是调用 VFS(虚拟文件系统)层的通用页高速缓存写入磁盘的操作。

具体来说,它会遍历给定映射(mapping)关联的所有脏页,将这些脏页写入对应的后备存储设备(通常是磁盘)。这个过程是同步执行的,即该函数直到所有脏页都被写入磁盘才会返回。

以上是XFS文件系统特有实现,而zfs实现不同,所以不存在同样的问题。

cp现象

在d6.bin存在的情况下,使用cp d.bin d6.bin进行拷贝,如果使用了truncate -s 0 d6.bin,会使拷贝速度急速上升的。为什么?

现象分析

cp a b慢

truncate -s 0 b; cp a b快

直接cp a b时对b文件打开是带O_TRUNC标志(d6.bin已存在),就会在close的时候进行同步下刷,速度就不会快;

而truncate -s 0 b; cp a b,cp对b的打开同样是带O_TRUNC标志的,但是由于在cp之前,执行了truncate的操作,根据xfs文件系统落盘的机制,在cp的时候O_TRUNC就不起作用了,也就是在close的时候不会进行同步下刷;=》内核会对同样的两次操作进行优化,避免做重复的工作。

xfs文件系统源码

先看紫色字的注释:

如果之前对该文件进行了截断操作,删除了旧的文件数据。我们希望在最后关闭该文件时,提前(early)将数据写出到磁盘。这个问题特别容易在以下情况发生:先截断文件,然后通过缓冲区写入(重写)数据(延迟分配delalloc),接着系统崩溃。我们在这里所做的,实际上是大大缩小了可能遇到这个问题的时间窗口。

所谓空文件问题,是指在截断、重写文件数据的过程中,如果系统崩溃,可能导致原有数据被删除,而新写入的数据还未持久化到磁盘,从而使文件内容变为空或不完整。

为了避免这种情况,XFS会在关闭经过截断和重写的文件时,主动提前将缓冲区数据刷新到磁盘。这样可以最大限度减小系统崩溃导致数据丢失的窗口期,从而提高数据完整性和一致性。

执行过程如下:

判断文件系统状态xfs_is_shutdown,执行以下操作:

a. 判断truncated状态:XFS_ITRUNCATED会把XFS_ITRUNCATED复位,返回true;如果flags里面没有XFS_ITRUNCATED置位,返回false;

b. 如果上一步标志被清除(即之前发生了截断 ),调用 xfs_iflags_clear(XFS_IDIRTY_RELEASE) 清除 XFS_IDIRTY_RELEASE 标志。该标志表示需要在卸载时将延迟写入的元数据写入磁盘。

c. 如果上一步标志被清除,调用 filemap_flush(VFS_I(ip)->i_mapping) 将所有延迟的元数据写入磁盘。(数据下刷

所以手动执行truncate命令时,变量truncated=false,if(truncated)函数就不会执行,也就没有数据下刷的操作,观测结果"看上去"cp速度很快。

验证

这个情况同样可以用sar观测I/O被证实:

相关推荐
腾讯TNTWeb前端团队6 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
mghio8 小时前
Dubbo 中的集群容错
java·微服务·dubbo
范文杰10 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪10 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪10 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy11 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom11 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom11 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom11 小时前
React与Next.js:基础知识及应用场景
前端·面试·github