有害的 USB 记忆棒停顿问题- LWN.net,2013 年 11 月。
Artem S. Tashkinov 最近遇到了一个问题,至少一些 LWN 读者会熟悉这个问题。将慢速存储设备(例如 U 盘或媒体播放器)插入 Linux 机器并向其写入大量数据。整个系统继续挂起,可能持续几分钟。
不过这一次,Artem 做了一个有趣的观察:系统在使用 64 位内核运行时会停顿,但在相同硬件上使用 32 位内核时没有遇到这样的问题。
文章解释说,对于 64 位内核,默认情况下允许脏页缓存(回写缓存)增长到内存的 20%。使用 32 位内核时,它实际上被限制在大约 180MB。
Linus 建议在 64 位上也将其限制为 ~180MB,但是当前的 Linux (v4.18) 没有这样做。将Linus 建议的补丁, 与Linux 4.18 中的当前功能进行比较。反对这种变化的最大论据来自 Dave Chinner。他指出,过多地减少缓冲会导致文件系统受到碎片化的影响。他还解释说,“对于流式 IO,我们通常需要至少 5 秒的缓存脏数据来平衡延迟。”
我很迷惑。优盘卡顿为什么会导致整个系统挂掉?
我很困惑,因为我读了一篇描述2011 年合并代码的早期文章(Linux 3.2)。它显示内核应该已经在每个设备的基础上控制脏页缓存:
无 I/O 脏节流- LWN.net, 2011
这就是Fengguang 的补丁集的用武之地。他试图创建一个控制循环,该循环能够确定在任何给定时间每个进程应该允许脏多少页。超出其限制的进程会被简单地休眠一段时间以允许写回系统赶上它们。
[...]
系统的目标是将脏页数保持在设定值;如果事情变得不合时宜,就会施加越来越大的力量,将事情带回应有的位置。
[...]
但是,如果不考虑支持设备 (BDI),则无法真正计算出该比率。一个进程可能正在弄脏存储在给定 BDI …
我有一个/dev/mydisk基于一系列功能的设备:LUKS 加密的软件 RAID-1。
有时,我/dev/mydisk会将内容备份到外部 USB 磁盘,该磁盘本身使用 LUKS 加密。需要传输几个 100 GiB。这个操作不是简单的dd而是递归的cp(我还是需要改用rsync)
备份开始一段时间后,整个系统的交互性急剧下降。KDE 接口显然在等待获得批准的内存请求而窒息而死。提示的等待时间为 2 分钟并不罕见。等待网络 I/O 同样需要很大的耐心。这与baloo启动并决定解压缩每个 zip 并为每个文件内容编制索引以用于未知目的时发生的行为类似:系统变成沼泽独木舟。
内核似乎将所有 RAM 提供给复制进程,并且不愿意将其交还给交互式进程一个机会。RAM 并不寒酸:23 GiB。还有 11 GiB 的交换空间,以防万一,但它随时都会被几个 MiB 占用。
是否可以确保交互式进程优先于复制进程获得它们的 RAM?如果是这样,如何?
版本信息:
感谢大家到目前为止的答案!
一旦知道要搜索什么,就会发现一些东西。
我拖进来的东西:
dd,补救方法是使用标志oflag=direct绕过页面缓存。我最近检查了一个几乎完全挂起的 RHEL7.2,因为它写入了 CIFS 文件系统。dirty_ratio = 30和 cifs的默认设置被缓存(用于读取和写入),这些脏页大多是 cifs 的。
在内存压力下,当系统回收大部分读缓存时,系统顽固地尝试刷新和回收脏(写)缓存。所以情况是一个巨大的 CPU iowait 伴随着极好的本地磁盘 I/O 完成时间,D 中的许多进程不间断等待和一个完全无响应的系统。OOM杀手从来没有从事过,因为是免费的内存系统没有给出来。(我认为 CIFS 也有一个错误,它使刷新速度慢得令人难以置信。但别介意这里。)
我惊讶地发现内核处理刷新页面到一些慢速远程 CIFS 盒的方式与处理超快速本地 SSD 驱动器完全相同。拥有一个dirty_ratio包是不明智的,它很快就会导致 30% 的 RAM 包含来自最慢设备的脏数据的情况。真是浪费钱。
这种情况是可重现的;设置dirty_ratio = 1彻底解决问题。但是为什么我需要因为使用cifs挂载而牺牲本地磁盘的缓存?
除了完全禁用某些设备的缓存或设置vm.dirty_ratio为非常低的值之外,还有什么方法可以将快速设备“列入白名单”以获得更多写入缓存?或者让慢速设备(或像 //cifs/paths 这样的远程“设备”)使用更少的写缓存?
RHEL 7.2 的内核版本称为 3.10.0-327。(它基于 3.10.0,但包括数年的向后移植)。