如何快速停止导致抖动(由于内存分配过多)的进程?

nib*_*bot 21 memory freeze swap ubuntu

我们都经历过它——一些程序被要求做一些需要大量内存的事情。它尽职尽责地尝试分配所有这些内存,系统立即开始抖动,无休止地交换并变得缓慢或无响应。

我最近在我的 Ubuntu 笔记本电脑上遇到了这个问题,因为一个 Matlab 脚本试图分配一个非常大的矩阵。经过约 5 分钟的颠簸后,我能够按 Ctrl-F1 到控制台并杀死 Matlab。我宁愿有一些热键,它可以让我立即控制系统并允许我终止有问题的进程;或者,也许只是默默地拒绝分配如此大的缓冲区。

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

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

jok*_*oke 14

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

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


tob*_*xen 6

我为此目的制作了一个脚本 - https://github.com/tobixen/thrash-protect

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

当然,“购买更多内存”或“不使用交换”是关于“如何避免颠簸?”问题的两种选择,更传统的答案,但总的来说,它们往往效果不佳(安装更多内存可能不是微不足道的,一个流氓进程可以吃掉所有的内存,无论安装了多少,当没有足够的内存用于缓冲/缓存时,即使没有交换,一个人也可能陷入颠簸问题)。我确实推荐 thrash-protect 以及大量交换空间。


JPv*_*iel 5

  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)
Run Code Online (Sandbox Code Playgroud)

它:

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

如您所见,当尝试请求超过 1GB 时它停止了。

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

永久保护

限制虚拟内存分配,as相当于-vfor limits.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"
Run Code Online (Sandbox Code Playgroud)

为了验证,这会导致以下结果(例如在 16GB 系统上):

$ cat /etc/security/limits.d/mem.conf
*   hard    as      16135196
$ ulimit -H -v
161351960
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 仅减轻单个进程过度使用内存的情况。
  • 不会阻止具有沉重内存压力的多进程工作负载导致颠簸(cgroups 就是答案)。
  • 不要rss在limits.conf 中使用选项。较新的内核不尊重它。
  • 这是保守的。
    • 理论上,一个进程可以推测性地请求大量内存,但只主动使用一个子集(较小的工作集/常驻内存使用)。
    • 上述硬限制将导致此类进程中止(即使它们本来可以正常运行,因为 Linux 允许过度使用虚拟内存地址空间)。

较新的 CGroups

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

  • 改进了 ulimit 产品。
    • memory.max_usage_in_bytes 可以分别核算和限制物理内存。
    • ulimit -m和/或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
...
Run Code Online (Sandbox Code Playgroud)

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

$ 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
Run Code Online (Sandbox Code Playgroud)

要查看它在运行时将 RAM 作为后台进程咀嚼然后被杀死:

$ 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'
Run Code Online (Sandbox Code Playgroud)

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

将来,让我们希望看到“发行版/供应商”为 SSH 和图形堆栈等重要事物预先配置 cgroup 优先级和限制(通过 systemd 单元),这样它们就永远不会出现内存不足的情况。