OOM killer不起作用了吗?

据我所了解,当系统接近没有可用内存时,内核应该开始终止进程以释放一些内存。但在我的系统中,这根本不会发生。
假设有一个简单的脚本,它只是分配比系统中可用内存更多的内存(例如,一个包含数百万个字符串的数组)。如果我运行这样的脚本(作为普通用户),它会一直占用所有内存,直到系统完全冻结(只有SysRQ REISUB可以工作)。
奇怪的是,当计算机冻结时,硬盘指示灯会亮起并保持这种状态,直到计算机重新启动,无论我是否挂载了交换分区!
因此,我的问题是:
1. 这种行为正常吗?一个作为普通用户执行的应用程序能够以这种方式使系统崩溃,这很奇怪... 2. 是否有任何办法让Ubuntu在这些应用程序占用过多(或最多)内存时自动终止它们?

附加信息

  • Ubuntu 12.04.3
  • Kernel 3.5.0-44
  • RAM: 约3.7GB,总共4GB(与显卡共享)。 *

    $ tail -n+1 /proc/sys/vm/overcommit_*
    ==> /proc/sys/vm/overcommit_memory <==
    0
    
    ==> /proc/sys/vm/overcommit_ratio <==
    50
    
    $ cat /proc/swaps
    Filename                Type        Size    Used    Priority
    /dev/dm-1                               partition   4194300 344696  -1
    

我不确定为什么它不起作用。尝试执行 tail -n+1 /proc/sys/vm/overcommit_* 并将输出添加进来。此外,也可以参考这里的链接:如何配置 oom-killer - kiri
你的交换空间出了什么问题?你能贴一些vmstat输出,比如#vmstat 1 100之类的吗? 还有,请给我们展示cat /etc/fstab的内容。应该发生的是在达到一定的内存使用量时,你应该开始写入交换空间。只有当内存和交换空间都“满”了才会杀掉进程。 - j0h
也尝试执行 #swapon -a - j0h
@j0h 使用交换分区后,它似乎运行良好(在一段时间后,进程崩溃并显示“分配失败”之类的错误)。但是如果没有交换分区,计算机就会完全卡住。这是预期的工作方式吗(只有在使用交换分区时才终止进程)? - Salem
PS:我试图不使用交换空间,因为我的硬盘非常非常慢,所以当某些东西开始交换时,我的电脑会卡住。 - Salem
请看这里,这是关于配置OOMKiller的文章:http://lwn.net/Articles/317814/ - j0h
或者,也许你可以在这个脚本中加入一个低内存时终止进程的选项,以避免内存耗尽。例如:http://pastebin.ubuntu.com/6694682/ - j0h
@Salem,请尝试在没有挂载交换空间的情况下执行echo 1 | sudo tee /proc/sys/vm/oom_kill_allocating_task命令。此操作将在重启后恢复原状。 - admirabilis
@TeresaeJunior 你能写成一个回答吗(还有如何使其永久化)?虽然它并不能完全修复,但我想这是我在这个硬件上能得到的最好结果了... - Salem
3使用SysRq,你还可以调用OOM(SysRq + F iirc) - Lekensteyn
@Salem 刚刚完成 :) - admirabilis
4个回答

官方/proc/sys/vm/*文档中可以得知:

oom_kill_allocating_task

这个选项用于启用或禁用在内存不足情况下杀死触发OOM的任务。

如果将其设置为零,OOM killer将扫描整个任务列表,并根据启发式算法选择一个任务进行杀死。通常情况下,会选择一个占用大量内存的恶意任务,在被杀死时释放大量内存。

如果将其设置为非零值,OOM killer将直接杀死触发内存不足条件的任务。这样可以避免昂贵的任务列表扫描。

如果选择了panic_on_oom,则其优先于oom_kill_allocating_task中使用的任何值。

默认值为0。

简而言之,当将oom_kill_allocating_task设置为1时,内核不会扫描系统以寻找要杀死的进程,这是一项昂贵且耗时的任务,而是直接杀死导致系统内存不足的进程。

根据我的经验,当触发OOM时,内核已经没有足够的"力量"来执行这样的扫描,使系统完全无法使用。
此外,杀死导致问题的任务会更明显,所以我不明白为什么默认设置为0
对于测试,您可以直接写入/proc/sys/vm/中的适当伪文件,这将在下次重启时撤消。
echo 1 | sudo tee /proc/sys/vm/oom_kill_allocating_task

为了永久解决问题,请将以下内容写入/etc/sysctl.conf或新文件中,该文件位于/etc/sysctl.d/下,扩展名为.conf(例如/etc/sysctl.d/local.conf):

vm.oom_kill_allocating_task = 1

3在Ubuntu中,它一直设置为0吗?因为我记得以前它会自动终止,但从几个版本开始,它停止这样做了。 - Jelle De Loecker
2@skerit 我不是很清楚这个问题,但在2010年我使用的内核中(Debian、Liquorix和GRML),它被设置为0。 - admirabilis
4此外,直接终止导致问题的任务会更加明显,所以我无法理解为什么默认设置为0。因为请求内存的进程不一定是“导致问题”的那个进程。如果进程A占用了系统99%的内存,但是运气不好的是使用了0.9%内存的进程B触发了OOM killer,那么B并没有“导致问题”,杀掉B就没有任何意义。将其作为策略可能会导致本来没有问题的低内存进程被其他进程的失控内存使用机会杀死,这是没有意义的。 - Mark Amery
4@MarkAmery 真正的问题是,Linux在需要终止进程时,会像一个智障一样开始频繁切换,即使将vm.admin_reserve_kbytes增加到了128 MB。设置vm.oom_kill_allocating_task = 1似乎能缓解问题,但并不能真正解决(而且Ubuntu默认已经处理了fork炸弹)。 - admirabilis
3也许更优雅的方式是使用命令 sudo sysctl -w vm.oom_kill_allocating_task=1 - Pablo Bianchi

更新:问题已修复。

Teresa的回答足以解决问题,而且很好。

此外,我已提交一个错误报告,因为这绝对是一个错误的行为。


2我不知道为什么你被踩了,但对我来说那也像是一个内核 bug。今天我用它导致了一台大学服务器崩溃,并杀死了一些已经运行了数周的进程... 不过还是谢谢你提交了那个 bug 报告! - shapecatcher
13可能在2014年已经修复了,但在2018年(以及18.04版本)中,OOM killer再次无效。 - Jelle De Loecker
1是的,还有21.04版本出了问题。我现在讨厌Ubuntu了。 - france1
1@france1 "虽然这个问题不会无限期挂起,有时系统在几分钟后能够恢复。",这是我在Ubuntu 20.04.4 LTS桌面版中观察到的情况,内核版本为5.15.0-46-generic。但是在一台名为Linux VM-32-17-ubuntu 5.4.0-121-generic且运行Ubuntu 20.04 LTS的VPS上,对于相同的测试,它没有恢复。 - Rick

你可以尝试使用earlyoom,这是一个在用户空间运行的OOM killer,它会尝试在内存不足的情况下终止最占用资源的进程。

1earlyoom运行得非常顺利。 - Rick

首先,我建议您升级到13.10版本(干净安装,请保存您的数据)。
如果您不想升级,请将vm.swappiness更改为10,如果您的内存出现问题,请安装zRAM。

2我不是给你点踩的那个人,但通常来说,降低 vm.swappiness 的值对系统的影响更多是负面的,尤其是在内存不足的情况下。 - admirabilis
不要先压缩内存,然后避免使用磁盘,因为这样会更慢,并可能导致计算机冻结。 - Brask
从理论上讲,zRAM是一件好事,但它对CPU的消耗较大,通常不值得这个代价。内存通常比电力便宜得多。而且,在笔记本电脑上,升级内存更加昂贵,CPU使用率大多数时候是不可取的。 - admirabilis
他所要求的是拥有一个更稳定的zRAM系统,改变swappiness会使他的系统使用更多的CPU资源,但他目前受限于内存,并且遇到了错误。他想解决问题,而不是听关于安装zRAM时发生什么的理论课。 - Brask
从他的问题中可以清楚地看出,他可能会编写一个不合适的脚本,消耗比应该多得多的资源(我自己也曾经这样做过)。在这种情况下,你会看到脚本在几秒钟内占用了几个GB的内存,而zRAM也无法解救,因为脚本永远不会满足。 - admirabilis
但尽可能减少磁盘使用和压缩内存将会极大地帮助改善问题 :) - Brask
减少磁盘使用和压缩内存只会延长问题的存在。你仍然可能遇到OOM(Out of Memory)的情况,而内核应该能够优雅地处理这个问题。然而事实证明,这是由于一个内核错误引起的(通过设置vm.oom_kill_allocating_task = 1可以解决此问题)。 - shapecatcher