我在旋转硬盘上运行 Linux。 WDC WD5000LPLX-7
,“WD Black Mobile”,7200 RPM。
我注意到一个简单的文件复制(或写入)会导致 fsync() 延迟超过十秒。在 Linux 上有什么方法可以避免这种情况吗?没有更换硬件或更改cp
命令[*]?或者没有其他方法可以避免这种情况吗?
[*] 如果我能够避免它使用 O_DIRECT 写入文件。
什么是 fsync() ?
https://thunk.org/tytso/blog/2009/03/15/dont-fear-the-fsync/
rename() + fsync() 用于自动更新文件,以在断电时安全。
建议应用程序开发人员使用单独的线程编写配置/状态更新,这样如果写入需要一段时间,他们就不会冻结用户界面。 (参见示例:在 gnome shell 中冻结)。但是,在保存用户文件时,此建议似乎不太有用。例如,当您使用终端中的编辑器一次编辑一个文件时 -vi my-file
编辑,wq
完成。自然vi
会等待 fsync() 完成后再退出。您可能更喜欢使用不同的编辑器,但我敢打赌您的编辑器会做同样的事情:-)。
测试设置
$ sudo -i
# lvcreate alan_dell_2016 -n test --extents 100%FREE
Logical volume "test" created.
# ls -l /dev/alan_dell_2016/test
lrwxrwxrwx. 1 root root 7 Feb 18 13:34 /dev/alan_dell_2016/test -> ../dm-3
$ uname -r
4.20.3-200.fc29.x86_64
$ cat /sys/block/sda/queue/scheduler
mq-deadline [bfq] none
$ cat /sys/block/dm-3/queue/scheduler
none
我使用 CFQ I/O 调度程序重现了 gnome-shell 冻结。 CFQ在下一个内核版本中消失无论如何,目前我已经将我的系统配置为使用 BFQ。
我也尝试过mq-deadline
调度程序。对于所有这些 I/O 调度程序,我发现 fsync() 延迟超过 10 秒。我的内核是使用 CONFIG_BLK_WBT_MQ=y 构建的。 (WBT 适用于截止日期调度程序;默认情况下不适用bfq
)。
# mkfs.ext4 /dev/alan_dell_2016/test
mke2fs 1.44.3 (10-July-2018)
Creating filesystem with 2982912 4k blocks and 746304 inodes
Filesystem UUID: 736bee3c-f0eb-49ee-b5be-de56ef1f38d4
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208
Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done
# mount /dev/alan_dell_2016/test /mnt
# cd /mnt
# df -h .
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/alan_dell_2016-test 12G 41M 11G 1% /mnt
测试运行
# dd if=/dev/zero of=writetest bs=1M count=5k conv=fsync & sleep 1; while true; do time sh -c 'echo 1 > latencytest; time sync latencytest; mv latencytest latencytest2'; sleep 1; killall -0 dd || break; done
[1] 17060
real 1m14.972s
user 0m0.001s
sys 0m0.000s
real 1m14.978s
user 0m0.005s
sys 0m0.002s
5120+0 records in
5120+0 records out
5368709120 bytes (5.4 GB, 5.0 GiB) copied, 75.9998 s, 70.6 MB/s
[1]+ Done dd if=/dev/zero of=writetest bs=1M count=5k conv=fsync
dd: no process found
# cp writetest copytest & sleep 3; while true; do time sh -c 'echo 1 > latencytest; time sync latencytest; mv latencytest latencytest2'; sleep 3; killall -0 cp || break; done
[1] 17397
real 0m59.479s
user 0m0.000s
sys 0m0.002s
[1]+ Done cp -i writetest copytest
real 0m59.504s
user 0m0.037s
sys 0m4.385s
cp: no process found
我想这涉及文件系统细节。如果我在块设备级别执行相同的操作,则延迟会低得多。
# cd / && umount /mnt
# dd if=/dev/zero of=/dev/alan_dell_2016/test bs=1M count=2000 conv=fsync &
[1] 6681
# dd if=/dev/zero of=/dev/alan_dell_2016/test oflag=sync bs=4096 count=1
1+0 records in
1+0 records out
4096 bytes (4.1 kB, 4.0 KiB) copied, 0.193815 s, 21.1 kB/s
答案1
这些延迟取决于文件系统。我在不同的文件系统上运行了测试。 ext3
两者ext4
都显示出一些“糟糕”的结果。 XFS、btrfs 和 bcachefs“没那么糟糕”。我没有收集严格的统计数据,但下一节中有一些说明性的数字。
更新:结果还取决于内核版本。一些补丁刚刚被接受ext4
,它们似乎与这个问题非常相关。耶!
我尝试在v5.2-rc5-293-g001da3fd278f
.运行测试,我没有看到任何十秒及以上的延迟。尽管有时也接近这一点。看起来有时可能比我的 XFS/btrfs/bcachefs 结果更糟糕。
免责声明:我的测试不是很科学:-)。在此更新中,我确保仍然可以重现“超过十秒的 fsync() 延迟”作为基准。但是,我的基线测试是在 kernel 上进行的5.1.6-200.fc29
,而不是在v5.2-rc5
.我只能重现第一个测试 ( writetest
) 中的长时间延迟,而不能重现第二个测试 ( copytest
) 中的长时间延迟。
后来,我尝试同时测试 Firefox 和copytest
. Firefox 的版本为 67.0.4(带有几个扩展)。 (内核是相同的 v5.2-rc5-293-g001da3fd278f,顶部有补丁,而基线为 5.1.11-200.fc29)。
我的 Firefox 测试是打开一个新选项卡google.com
。在基线上有几十秒的延迟,看起来像是可能与类似的长 fsync 延迟相关。和我思考修补后的内核修复了 Firefox 中这十多秒的延迟。
所以我对这些补丁抱有很大的希望!
也就是说,在达到copytest
10G 左右之后,整个 GUI 响应能力开始下降,包括鼠标光标。我强制它允许使用 Alt+SysRQ+R 进行 VT 切换。几次 VT 来回切换后,GUI 崩溃了。
崩溃的日志消息附在本答案的末尾。可能 GUI 不喜欢这样被强迫。或者,它可能交换引起的延迟会触发 Wayland 合成器和/或 XWayland 中的某些竞争条件,可能类似于已知错误哪个 gnome-shell“试图避免”:-(。
Ted T'so 也一直在抱怨对 ext4 日志进行更长期的改变。所以也许,在未来的某个时候,ext4 还会有更多的改进!
[PATCH v2 2/3] jbd2:引入 jbd2_inode 脏范围范围
目前,journal_submit_inode_data_buffers() 和journal_finish_inode_data_buffers() 都在与给定日志条目关联的每个inode 的整个地址空间上运行。这样做的结果是,如果我们有一个不断附加脏页的索引节点,那么当我们等待写回的所有页被写出时,我们最终可能会在journal_finish_inode_data_buffers()中等待无限长的时间。
导致此类工作负载的最简单方法是从 /dev/zero 到文件执行 dd 操作,直到它填满整个文件系统。这可能会导致journal_finish_inode_data_buffers()等待整个dd操作的持续时间。
我们可以通过确定与给定事务关联的每个 inode 脏范围的范围来改善这种情况。我们通过 jbd2_inode 结构来执行此操作,以便范围界定包含在 jbd2 内,并遵循该结构的生命周期和锁定规则。
这允许我们将journal_submit_inode_data_buffers()和journal_finish_inode_data_buffers()中的写回和等待分别限制在给定结构jdb2_inode的脏范围内,如果仍有问题的inode仍然被附加到,那么我们就不会永远等待。
不同文件系统上的(旧)结果
这些测试是使用从 bcachefs 树构建的内核(版本 v4.20-297-g2252e4b79f8f (2019-02-14))执行的。
另请参阅上面更新中的免责声明。我无法在 v5.1.6 上重现所有 ext4 结果。要么是我的测试方法不可靠,要么是升级到 v5.1.6 时发生了重大变化。
ext4 bfq: writetest saw 15s, 30s. copytest 10s, 40s.
ext4 mq-deadline: writetest saw 10s, 30s. copytest 5s, 45s.
ext3 bfq: writetest saw 20s, 40s. copytest ~0.2s, once saw 0.5s and 2s.
ext3 mq-deadline: writetest saw 50s. copytest ~0.2s, very occasionally 1.5s.
ext3 mq-deadline, wbt disabled: writetest saw 10s, 40s. copytest similar to the above.
ext2 bfq: writetest 0.1 - 0.9s. copytest ~0.5s.
ext2 mq-deadline: writetest 0.2 - 0.6s. copytest ~0.4s
xfs bfq: writetest 0.5 - 2s. copytest 0.5 - 3.5s.
xfs mq-deadline: writetest 0.2s, some 0.5s. copytest 0 - 3s.
bcachefs bfq: writetest 1.5 - 3s.
bcachefs mq-deadline: writetest 1 - 5s.
btrfs bfq: writetest 0.5-2s, copytest 1 - 2s.
btrfs mq-deadline: writetest ~0.4s, copytest 1 - 4s.
复制文件时 ext3 的数字看起来更好,但 ext3 是不是一般来说,对于延迟来说是个好主意(例如,请参阅 tytso 链接:-)。 ext2 缺乏日志记录 - 日志记录通常对于鲁棒性来说是可取的,但 ext 日志记录是导致这种延迟的原因。
因此,我最感兴趣的替代方案是 XFS、实验性 bcachefs 和 btrfs。我认为 XFS 是最容易使用的,至少在机械硬盘上是这样。一个显着的区别是没有工具可以缩小 XFS 文件系统,只能增大它们。
图形用户界面崩溃
与上述“GUI 崩溃”对应的日志消息。
(这次崩溃后不久,gdm 问候程序也崩溃了。尽管这些消息有点不同。)
Jun 26 22:27:25 alan-laptop gnome-shell[1814]: Failed to set CRTC mode 1366x768: Permission denied
Jun 26 22:27:25 alan-laptop gnome-shell[1814]: Failed to disable CRTC
Jun 26 22:27:25 alan-laptop gnome-shell[1814]: Failed to disable CRTC
Jun 26 22:27:26 alan-laptop gnome-shell[1814]: WL: unknown object (31), message get_swipe_gesture(no)
Jun 26 22:27:26 alan-laptop gnome-shell[1814]: WL: error in client communication (pid 2259)
Jun 26 22:27:26 alan-laptop gnome-shell[1814]: WL: unknown object (28), message get_swipe_gesture(no)
Jun 26 22:27:26 alan-laptop gnome-shell[1814]: WL: error in client communication (pid 1896)
Jun 26 22:27:27 alan-laptop org.gnome.Shell.desktop[1814]: (EE)
Jun 26 22:27:27 alan-laptop org.gnome.Shell.desktop[1814]: Fatal server error:
Jun 26 22:27:27 alan-laptop org.gnome.Shell.desktop[1814]: (EE) wl_display@1: error 1: invalid arguments for [email protected]_relative_pointer
Jun 26 22:27:27 alan-laptop org.gnome.Shell.desktop[1814]: (EE)
Jun 26 22:27:26 alan-laptop gnome-shell[1814]: WL: unknown object (28), message get_swipe_gesture(no)
Jun 26 22:27:26 alan-laptop gnome-shell[1814]: WL: error in client communication (pid 2257)
Jun 26 22:27:27 alan-laptop gnome-shell[1814]: WL: unknown object (237), message get_relative_pointer(no)
Jun 26 22:27:27 alan-laptop gnome-shell[1814]: WL: error in client communication (pid 1814)
Jun 26 22:27:28 alan-laptop audit[1842]: ANOM_ABEND auid=1000 uid=1000 gid=1000 ses=2 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 pid=1842 comm="Xwayland" exe="/usr/bin/Xwayland" sig=6 res=1
Jun 26 22:27:27 alan-laptop gnome-shell[1814]: clutter_actor_set_reactive: assertion 'CLUTTER_IS_ACTOR (actor)' failed
Jun 26 22:27:27 alan-laptop gnome-shell[1814]: clutter_actor_set_reactive: assertion 'CLUTTER_IS_ACTOR (actor)' failed
Jun 26 22:27:27 alan-laptop gnome-shell[1814]: clutter_actor_set_reactive: assertion 'CLUTTER_IS_ACTOR (actor)' failed
Jun 26 22:27:27 alan-laptop gnome-shell[1814]: WL: unknown object (28), message get_swipe_gesture(no)
Jun 26 22:27:27 alan-laptop gnome-shell[1814]: WL: error in client communication (pid 2251)
Jun 26 22:27:27 alan-laptop unknown[2257]: Error 22 (Invalid argument) dispatching to Wayland display.
Jun 26 22:27:27 alan-laptop gnome-shell[1814]: clutter_input_device_get_device_type: assertion 'CLUTTER_IS_INPUT_DEVICE (device)' failed
Jun 26 22:27:27 alan-laptop gnome-shell[1814]: JS ERROR: TypeError: device is null
_init/<@resource:///org/gnome/shell/ui/keyboard.js:615:1
Jun 26 22:27:27 alan-laptop unknown[2251]: Error 22 (Invalid argument) dispatching to Wayland display.
Jun 26 22:27:31 alan-laptop systemd[1]: Created slice system-systemd\x2dcoredump.slice.