如何从命令行测试 oom-killer

如何从命令行测试 oom-killer

OOM 杀手或内存不足杀手是一个过程 Linux的当系统内存严重不足时,内核采用这种方法。... 通过确保分配给进程的内存被积极使用,可以最大限度地利用系统内存。

这个自我回答的问题是:

  • 如何从命令行测试 oom-killer?

比自答所花费的半小时更快的方法将被接受。

答案1

快速触发 OOM 杀手的关键是避免因磁盘访问而陷入困境。因此:

  1. 避免交换,除非您的目标是专门测试使用交换时 OOM 的行为。您可以在测试之前禁用交换,然后在测试之后重新启用它。swapon -s告诉您当前启用了哪些交换。sudo swapoff -a禁用所有交换;sudo swapon -a通常足以重新启用它们。

  2. 避免将内存访问与非交换磁盘访问交织在一起。 基于通配符的方法最终会用尽可用内存(假设文件系统中有足够多的条目),但它需要这么多内存的原因是为了存储通过访问文件系统获得的信息。即使使用 SSD,即使关闭交换,也可能会花费大量时间从磁盘读取。如果您的目标是专门测试与磁盘访问交错的内存访问的 OOM 行为,那么该方法是合理的,甚至可能是理想的。否则,您可以更快地实现目标。

禁用交换后,任何很少从物理磁盘读取的方法都应该相当快。这包括tail /dev/zero成立经过福斯塔夫以上评论经过道格·史密斯)。虽然它从字符设备读取/dev/zero,但该“设备”只会生成空字节(即全为零的字节),并且一旦打开设备节点,就不会涉及任何物理磁盘访问。该方法有效,因为它会tail在其输入中查找尾随行,但零流不包含换行符,因此它永远不会得到任何要丢弃的行。

如果你是寻找解释型语言中的一行代码它会以算法方式分配和填充内存,那么您很幸运。在几乎任何通用解释型语言中,分配大量内存并写入内存而不以其他方式使用它都很容易。这里有一个 Perl 单行代码,它似乎和以下代码一样快tail /dev/zero(尽管我还没有对它进行广泛的基准测试):

perl -wE 'my @xs; for (1..2**20) { push @xs, q{a} x 2**20 }; say scalar @xs;'

在一台具有 4 GiB RAM 的旧机器上关闭交换后,tail /dev/zero每次运行这两个命令大约需要 10 秒钟。在具有更多 RAM 的新机器上,这两个命令应该仍能正常工作。如果您的目标是简洁,则可以将该perl命令缩短很多。

Perl 的一行代码重复生成(q{a} x 2**20)单独的中等长度的字符串(每个大约一百万个字符),并通过将它们存储在数组(@xs)中来保留它们。您可以调整数字以进行测试。如果您没有使用所有可用内存,则一行代码会输出创建的字符串总数。假设 OOM 终止程序确实终止了perl(使用上面显示的确切命令并且没有资源配额阻碍,我相信在实践中它总是会终止的),那么您的 shell 应该会向您显示Killed。然后,就像在任何 OOM 情况下一样,dmesg有详细信息。

虽然我喜欢这种方法,但它确实说明了编写、编译和使用 C 程序的一些有用的东西——就像Doug Smythies 的回答。在高级解释语言中,分配内存和访问内存看起来并不是分开的事情,但在 C 中,您可以注意到并且(如果您愿意)研究这些细节。


最后,你应该经常检查 OOM killer 是否真的是杀死你程序的那个。一种检查方法是检查dmesg。与普遍看法相反,即使在 Linux 上,尝试分配内存也可能会快速失败。这种情况很容易发生,因为大量分配显然会失败……但即使是这些分配也可能出乎意料地发生。看似合理的分配也可能很快失败。例如,在我的测试机器上,perl -wE 'say length q{a} x 3_100_000_000;'成功并perl -wE 'say length q{a} x 3_200_000_000;'打印:

Out of memory!
panic: fold_constants JMPENV_PUSH returned 2 at -e line 1.

两者都没有触发 OOM 杀手。更一般地说:

  • 如果您的程序预先计算需要多少内存并在一次分配中请求,则分配可能会成功(如果成功,当使用了足够的内存时,OOM 终止程序可能会或可能不会终止该程序),或者分配可能只是失败。
  • 通过添加大量元素来扩展数组的长度,在实际操作中经常会触发 OOM killer,但让它这样做可靠地在测试中,这一点出奇地棘手。几乎总是采用这种方式——因为这是最有效的方法——让每个新缓冲区都具有容量X倍旧缓冲区的容量。X包括 1.5 和 2(这种技术通常称为“表加倍”)。这有时会弥补实际可以分配和使用的内存量与内核知道的太多以至于甚至懒得假装分配的内存量之间的差距。
  • 内存分配失败的原因可能与内核或实际可用内存量无关,也不会触发 OOM 杀手。具体来说,在成功执行大量微小分配后,程序可能会在任何大小的分配上快速失败。这种失败发生在程序本身执行的簿记中——通常通过库工具(如) 。我怀疑今天发生在我身上的事情就是如此,在使用数组(实际上是作为双向链表实现的)malloc()进行测试时,程序退出时出现一条错误消息,指出分配bashbash9 个字节失败的。

意外触发 OOM 杀手比故意触发要容易得多。

在尝试故意触发 OOM 杀手时,解决这些问题的一种方法是首先请求过多的内存,然后逐渐减少,因为Doug Smythies 的 C 程序另一种方法是分配一大堆中等大小的内存块,这就是上面显示的 Perl 单行代码所做的:没有一个百万字符的字符串(加上后台的一些额外内存使用)特别费力,但加在一起,所有一兆字节的购买量加起来就很可观了。

答案2

该答案使用 C 程序分配尽可能多的内存,然后逐渐实际使用它,导致 OOM 保护被“杀死”。

/*****************************************************************************
*
* bla.c 2019.11.11 Smythies
*       attempt to invoke OOM by asking for a rediculous amount of memory
*       see: https://askubuntu.com/questions/1188024/how-to-test-oom-killer-from-command-line
*       still do it slowly, in chunks, so it can be monitored.
*       However simplify the original testm.c, for this example.
*
* testm.cpp 2013.01.06 Smythies
*           added a couple more sleeps, in attempts to observe stuff on linux.
*
* testm.cpp 2010.12.14 Smythies
*           attempt to compile on Ubuntu Linux.
*
* testm.cpp 2009:03:18 Smythies
*           This is not the first edit, but I am just adding the history
*           header.
*           How much memory can this one program ask for and sucessfully get?
*           Done in two calls, to more accurately simulate the program I
*           and wondering about.
*           This edit is a simple change to print the total.
*           the sleep calls have changed (again) for MS C version 2008.
*           Now they are more like they used to be (getting annoying).
*                                                                     Smythies
*****************************************************************************/

#include <stdio.h>
#include <stdlib.h>

#define CR 13

int main(){
   char *fptr;
   long i, k;

   i = 50000000000L;

   do{
      if(( fptr = (char *)malloc(i)) == NULL){
         i = i - 1000;
      }
   }
   while (( fptr == NULL) && (i > 0));

   sleep(15);  /* for time to observe */
   for(k = 0; k < i; k++){   /* so that the memory really gets allocated and not just reserved */
      fptr[k] = (char) (k & 255);
   } /* endfor */
   sleep(60);  /* O.K. now you have 1 minute */
   free(fptr); /* clean up, if we get here */
   return(0);
}

结果:

doug@s15:~/c$ ./bla
Killed
doug@s15:~/c$ journalctl -xe | grep oom
Nov 11 16:08:24 s15 kernel: mysqld invoked oom-killer: gfp_mask=0x100cca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0
Nov 11 16:08:25 s15 kernel:  oom_kill_process+0xeb/0x140
Nov 11 16:08:27 s15 kernel: [  pid  ]   uid  tgid total_vm      rss pgtables_bytes swapents oom_score_adj name
Nov 11 16:08:27 s15 kernel: oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,global_oom,task_memcg=/user/doug/0,task=bla,pid=24349,uid=1000
Nov 11 16:08:27 s15 kernel: Out of memory: Killed process 24349 (bla) total-vm:32638768kB, anon-rss:15430324kB, file-rss:952kB, shmem-rss:0kB, UID:1000 pgtables:61218816kB oom_score_adj:0
Nov 11 16:08:27 s15 kernel: oom_reaper: reaped process 24349 (bla), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB

它仍然需要一段时间才能运行,但只需几分钟。在 C 程序中
使用mlock可能会有帮助,但我没有尝试。

我的测试计算机是一台服务器,所以我用它watch -d free -m来监控进度。

读者:处理 OOM 有点危险。如果你阅读了所有这些答案和评论,你会注意到一些附带损害和不一致之处。我们无法控制其他任务何时会要求更多内存,这很可能是在错误的时间。请谨慎行事,并建议在进行此类测试后重新启动计算机。

答案3

在终端中输入“python”

然后复制并粘贴此代码并按 Enter:

var=[]
for x in xrange(99999999999):
    var.append(str(x))

然后做:

"cat /var/log/messages" and you'll find something like:
Nov 12 11:48:05 TestVM kernel: Out of memory: Kill process 1314 (python) score 769 or sacrifice child
Nov 12 11:48:05 TestVM kernel: Killed process 1314 (python) total-vm:1001264kB, anon-rss:802972kB, file-rss:60kB, shmem-rss:0kB
Nov 12 11:48:49 TestVM kernel: python[1337]: segfault at 24 ip 00007f2ad140c0da sp 00007ffee8c11820 error 6 in libpython2.7.so.1.0[7f2ad1382000+17e000]

答案4

如果您只是想触发 oom-killer,只需成倍增加“$a”的大小,如下所示:

bash -c "for b in {0..99999999}; do a=$b$a; done"

如果您想实时监控它,您只需要做一个嵌套循环,如下:

for x in {1..200}; do echo "Round $x"; bash -c "for b in {0..99999999}; do a=$b$a; done"; done

无需编译任何东西。Bash 可以自行完成。

预期成绩:

kernel: Out of memory: Kill process 1439 (bash) score 777 or sacrifice child
kernel: Killed process 1439 (bash)

注:不幸的是,我没有分数来发表此评论。

相关内容