老式的 ulimit

老式的 ulimit

我们都经历过这种情况——某个程序被要求执行需要大量内存的操作。它尽职尽责地尝试分配所有内存,系统立即开始不稳定,无休止地交换,变得迟缓或无响应。

我最近在 Ubuntu 笔记本电脑上遇到了这种情况,原因是 Matlab 脚本试图分配一个非常大的矩阵。经过大约 5 分钟的反复操作后,我能够按 Ctrl-F1 进入控制台并终止 Matlab。我更希望有一些热键可以让我立即控制系统并允许我终止有问题的进程;或者,也许只是默默地拒绝分配这么大的缓冲区。

  1. 对于由于过度交换而变得无响应或极其缓慢的 Linux 系统,重新获得控制权的最快方法是什么?

  2. 有没有有效的方法可以从一开始就防止这种交换的发生,例如通过限制进程允许尝试分配的内存量?

答案1

Alt-SysRq-F终止使用最多内存的进程:

  • SysRq 键通常映射到 Print 键。
  • 如果你使用的是图形桌面,你可能需要按Ctrl-Alt-SysRq-F如果按下Alt-SysRq触发另一个动作(例如快照程序)。
  • 如果您使用的是笔记本电脑,您可能还需要按下功能键。
  • 欲了解更多信息,请阅读维基百科文章

答案2

我为此编写了一个脚本 -https://github.com/tobixen/thrash-protect

我已经在生产服务器、工作站和笔记本电脑上运行了这个脚本,并且取得了良好的效果。这个脚本不会终止进程,但会暂时暂停它们 - 后来我遇到过几次这种情况,如果不是这个简单的脚本,我很确定我会因为抖动而失去控制。在“最坏”的情况下,有问题的进程会减慢很多,最终被内核(OOM)杀死,在“最好”的情况下,有问题的进程实际上会完成...无论如何,服务器或工作站将保持相对响应,以便于调查情况。

当然,“购买更多内存”或“不使用交换”是“如何避免系统抖动?”这个问题的两个备选、更传统的答案,但一般来说,它们往往效果不佳(安装更多内存可能并不简单,无论安装了多少内存,恶意进程都可能耗尽所有内存,即使没有交换,当没有足够的内存进行缓冲/缓存时,也可能会陷入系统抖动问题)。我确实建议使用抖动保护以及大量的交换空间。

答案3

  1. 对于由于过度交换而变得无响应或极其缓慢的 Linux 系统,重新获得控制权的最快方法是什么?

上面已经回答了Alt-SysRq-F

  1. 有没有有效的方法可以从一开始就防止这种交换的发生,例如通过限制进程允许尝试分配的内存量?

我正在回答第二部分。是的,ulimit仍然可以很好地限制单个进程。您可以:

  • 为可能失控的进程设置软限制
  • 如果你想要额外的保险,请为所有流程设置硬限制

另外,正如简要提到的:

您可以使用 CGroups 来限制资源使用并防止此类问题

确实,cgroups 提供了更高级的控制,但在我看来目前配置起来更为复杂。

老式的 ulimit

一旦脱落

这是一个简单的例子:

$ bash
$ ulimit -S -v $((1*2**20))
$ r2(){r2 $@$@;};r2 r2
bash: xmalloc: .././subst.c:3550: cannot allocate 134217729 bytes (946343936 bytes allocated)

它:

  • 设置 1GB 总体内存使用的软限制(ulimit 假设以 kB 为单位进行限制)
  • 运行递归 bash 函数调用r2(){ r2 $@$@;};r2 r2,该函数在请求堆栈内存时会无限加倍,从而成倍地消耗 CPU 和 RAM。

正如您所看到的,当尝试请求超过 1GB 时它就停止了。

注意,-v对虚拟内存分配(总计,即物理+交换)进行操作。

永久保护

限制虚拟内存分配,as相当于-vlimits.conf

我采取以下措施来防止出现任何单一行为不当的进程:

  • 为所有进程设置硬地址空间限制。
  • address space limit = <physical memory> - 256MB
  • 因此,没有任何一个贪婪使用内存或活动循环和内存泄漏的进程可以消耗所有物理内存。
  • 有 256MB 的余量可用于使用 ssh 或控制台进行基本处理。

一句话:

$ sudo bash -c "echo -e \"*\thard\tas\t$(($(grep -E 'MemTotal' /proc/meminfo | grep -oP '(?<=\s)\d+(?=\skB$)') - 256*2**10))\" > /etc/security/limits.d/mem.conf"

验证结果如下(例如在 16GB 系统上):

$ cat /etc/security/limits.d/mem.conf
*   hard    as      16135196
$ ulimit -H -v
161351960

笔记:

  • 仅能缓解单个进程内存使用过度的情况。
  • 无法阻止具有巨大内存压力的多进程工作负载导致的抖动(答案是 cgroups)。
  • 不要使用rsslimits.conf 中的选项。较新的内核不尊重它。
  • 这是保守的。
    • 理论上,一个进程可以推测性地请求大量内存,但只能主动使用其中的一小部分(较小的工作集/驻留内存使用)。
    • 上述硬限制将导致此类进程中止(即使在 Linux 允许虚拟内存地址空间过量使用的情况下它们可能运行良好)。

较新的 CGroups

提供更多控制,但目前使用起来更复杂:

  • 改进了 ulimit 产品。
    • memory.max_usage_in_bytes可以单独核算和限制物理内存。
    • ulimit -mand/or rssinlimits.conf旨在提供类似的功能,但自内核 Linux 2.4.30 以来就不再起作用了!
  • 需要在引导加载程序中启用一些内核 cgroup 标志:cgroup_enable=memory swapaccount=1
    • 在 Ubuntu 16.04 中默认情况下不会发生这种情况。
    • 可能是由于额外的会计开销对一些性能产生了影响。
  • cgroup/systemd 内容相对较新,变化也相当大,因此上游的变化意味着 Linux 发行版供应商尚未使其易于使用。在 14.04LTS 和 16.04LTS 之间,使用 cgroups 的用户空间工具已经发生了变化。
    • cgm现在似乎是官方支持的用户空间工具。
    • systemd 单元文件似乎还没有任何预定义的“供应商/发行版”默认值来优先考虑 ssh 等重要服务。

例如检查当前设置:

$ echo $(($(cat /sys/fs/cgroup/memory/memory.max_usage_in_bytes) / 2**20)) MB
11389 MB
$ cat /sys/fs/cgroup/memory/memory.stat
...

例如限制单个进程的内存:

$ cgm create memory mem_1G
$ cgm setvalue memory mem_1G memory.limit_in_bytes $((1*2**30))
$ cgm setvalue memory mem_1G memory.memsw.limit_in_bytes $((1*2**30))
$ bash
$ cgm movepid memory mem_1G $$
$ r2(){ r2 $@$@;};r2 r2
Killed

要查看它作为后台进程消耗内存然后被杀死的具体情况,请执行以下操作:

$ bash -c 'cgm movepid memory mem_1G $$; r2(){ r2 $@$@;};r2 r2' & while [ -e /proc/$! ]; do ps -p $! -o pcpu,pmem,rss h; sleep 1; done
[1] 3201
 0.0  0.0  2876
 102  0.2 44056
 103  0.5 85024
 103  1.0 166944
 ...
98.9  5.6 920552
99.1  4.3 718196
[1]+  Killed                  bash -c 'cgm movepid memory mem_1G $$; r2(){ r2 $@$@;};r2 r2'

请注意内存请求的指数(2的幂)增长。

在未来,我们希望看到“发行版/供应商”预先配置 cgroup 优先级和限制(通过 systemd 单元)以用于 SSH 和图形堆栈等重要事项,这样它们就不会缺少内存。

答案4

您可以使用 CGroups 来限制资源使用并防止此类问题: https://en.wikipedia.org/wiki/Cgroups

相关内容