![为什么 I/O 会导致我的系统几乎无响应,以及如何修复它?](https://linux22.com/image/164047/%E4%B8%BA%E4%BB%80%E4%B9%88%20I%2FO%20%E4%BC%9A%E5%AF%BC%E8%87%B4%E6%88%91%E7%9A%84%E7%B3%BB%E7%BB%9F%E5%87%A0%E4%B9%8E%E6%97%A0%E5%93%8D%E5%BA%94%EF%BC%8C%E4%BB%A5%E5%8F%8A%E5%A6%82%E4%BD%95%E4%BF%AE%E5%A4%8D%E5%AE%83%EF%BC%9F.png)
我正在从硬盘恢复数据。磁盘工作状态良好;一组文件被意外删除。我正在使用 photorec,并将恢复的文件存储在另一个物理磁盘上。我的操作系统和交换位于与这些不同的设备上。
然而,运行 photorec 会使我的系统基本上无法使用。仅使用 alt-tab(或以其他方式切换)到另一个窗口就需要 5-10 秒,鼠标迟缓且不稳定,并且程序通常没有响应。我一直不得不暂停输出(Ctrl-S,photorec 在 Konsole 内运行)才能完成任何事情。一旦我恢复输出,系统就开始变慢,一分钟之内它又几乎无法使用。
我已将 IO 调度类设置为空闲并将nice
值设置为 19(),但这种情况仍然不断发生。我有 32GB(!!!) 的 RAM,其中使用的不超过三分之一。我有一个带 HT 的四核 Xeon,并且没有使用 CPU(当 photorec 暂停时,CPU 使用率在空闲时几乎持平)。两个磁盘均显示出超过 100MB/s 的持续传输速率,因此硬件性能不应该出现问题。然而,在 CPU+IO 负载较高的情况下,该系统的响应速度比我的旧 Windows 98 机器要慢。
究竟为什么会发生这种情况,以及如何补救?
操作系统是 Debian 10 (buster),运行原版 4.19 内核。
即使我有 10 个线程/进程在全力运行(编码视频或编译大型项目没有问题),该系统也完全可用,我遇到的并不是由于负载而导致的普通减速。
答案1
让我们看看您问题中的一些内容:
一旦我恢复输出,系统就开始变慢,一分钟之内它又几乎无法使用。
这暗示某些内容正在备份一段时间,最终无法维持其吞吐量。既然您说它与其他所有内容位于不同的磁盘上,那么它应该是系统全局的。
我有 32GB(!!!) 的 RAM,其中使用的不超过三分之一。
我认为“使用”的意思是“不是 RSS”。不幸的是,事情并没有那么简单——而页面缓存是比 RSS 更容易释放,这并不意味着它们是免费的——例如,它们可能很脏并且需要首先刷新回磁盘——这个问题可能就是一个例子。 :-)
这通常是 a.) 使用的 I/O 调度程序和 b.) 正在执行的 I/O 类型的问题。在您的情况下,这可能是大量的页面缓存写回,一旦启动,内核通常不会轻易对其进行限制。即使这些写入可能位于不同的磁盘上,您仍然拥有页面缓存形式的单一共享状态源。
从意义上来说ionice
,I/O 调度类仅对 CFQ I/O 调度程序有任何影响,对其他调度程序没有任何影响。然而,CFQ 有大量权衡,倾向于“公平”而不是“延迟”,这可能会导致类似的情况。
CFQ 基于每个 TID 模型,其中每个线程都有自己的队列。然后,内核对这些队列进行循环处理,从每个队列中弹出一些项目,对它们进行操作,然后继续其愉快的方式——每个进程队列的这种有保证的操作是 CFQ 的“公平”部分。然而,公平并不一定等于性能,因为这意味着每个进程通常都会获得相同的优先级(除了 ionice 之类的调整)。
相比之下,顾名思义,截止日期是基于对每个 I/O 请求施加延迟超时。它不是专注于 TID 级别的公平性,而是专注于许多其他问题——主要是避免请求匮乏(通过每个操作类型的变量过期)以及通过将系统视为一个单元而不是将每个进程视为一个单元。为“公平”而运作。
我强烈建议尝试以下操作:
- 将 I/O 调度程序设置为 mq-deadline。一般来说,Deadline 比 CFQ 能更好地确保读取不会变得匮乏,这可以避免当某些内容最终访问有问题的磁盘时出现此类多秒的停顿。在桌面使用的情况下,当您期望事物能够响应时,您主要是在进行读取,因此这是有道理的。
- 考虑使用 cgroup v2's
io.latency
,我在其中稍微介绍了一下这次演讲。这是系统范围的,而不是每个设备的,并且允许您对 I/O 保护和优先级设置比使用 CFQ 更细粒度的控制ionice
。然后,您可以在需要低延迟 I/O 的 cgroup 中运行桌面,并使用systemd-run
等方法在另一个 cgroup 中运行数据恢复,而无需此类保护。这也使我们能够在这些“不可阻挡的”写入(例如页面缓存写回)进行之前先于它们进行,从而在某种程度上回退这些写入。 - 内核内存回收有两种类型:直接回收和kswapd回收。 kswapd 回收是我们试图避免系统内存使用(包括缓存!)达到 100% 的地方。这避免了我们进入回收的下一阶段,即直接回收。当应用程序请求内存但根本没有足够的内存来满足其请求时,就会发生直接回收。这实际上导致暂停受影响的应用程序,这可能会导致您所描述的那种停顿。如果您在这些期间看到大量直接回收(
grep allocstall /proc/vmstat
显示这些),则可能值得测试是否将 kswapd 回收的边界降低可以改善情况。您可以使用vm.watermark_scale_factor
sysctl 来完成此操作 - 请参阅这里有关如何使用它的文档。