我试图通过手动调用来查看oom_kill的工作。
我动态分配内存,并尝试首先在while循环中无限使用它们,然后在for循环中使用它们以测试内存不足。
但是,在我使用while循环的第一种情况下,它在没有交换的情况下引发了分段错误,并且对交换没有响应,而在内存不足的情况下调用了for循环(oom_kill)。
两者的样本代码:
第一种情况:while:
int main (void) {
char* p;
while (1) {
p=malloc(1<<20);
memset (p, 0, (1<<20));
}
}
第二种情况:针对:
int main (void) {
int i, n = 0;
char *pp[N];
for (n = 0; n < N; n++) {
pp[n] = malloc(1<<20);
if (pp[n] == NULL)
break;
}
printf("malloc failure after %d MiB\n", n);
for (i = 0; i < n; i++) {
memset (pp[i], 0, (1<<20));
printf("%d\n", i+1);
}
其中N是调用oom的非常大的数字。将此https://www.win.tue.nl/~aeb/linux/lk/lk-9.html用于第二种情况。
为什么会这样?我在while循环中犯了什么错误?
内核版本:4.15
答案 0 :(得分:2)
您正在阅读的文档来自2003年。它选择分配的数量过大是10,000 MiB。
今天,在2018年,当新计算机可能配备16GiB RAM时,这种分配肯定可以毫无问题地成功。
答案 1 :(得分:2)
while循环在犯什么错误?
分段错误可能是由于将空指针传递给 memset()的结果,因为 malloc()会在错误时返回NULL。
您的第二个示例通过始终检查 malloc()的返回值来避免此错误。
我使用了while循环...它对交换没有响应...
从您提到的正在阅读的文档中:
有时进程在访问内核无法提供的内存时遇到段错误,有时它们被杀死,有时其他进程被杀死,有时内核挂起。 < / p>
除了提及内核版本外,您对操作系统和系统说明也很含糊。大概是32位版本?
实际上有两种耗尽内存的方法。您的程序可能超出了分配的(虚拟)内存量,否则系统实际上可能会用完内存页面。
请注意,内存(页面)的可用性是物理内存大小,交换空间大小,内存使用情况和进程负载的复杂组合。
参考:When Linux Runs Out of Memory
,由Mulyadi Santosa或here。
答案 2 :(得分:2)
为什么会这样?
要调用OOM杀手,您需要遇到一种情况,即由于没有足够的RAM来实现访问而无法实现对内存的访问。为此,您首先要分配较大的空间(虚拟内存映射),然后将其写入。
触发OOM杀手的过程非常简单:
分配大量内存
写入分配的内存
在内核将唤起OOM杀手提供更多RAM /交换空间之前,您必须具有足够的预分配内存以使所有可从RAM收回的东西(如内存映射的文件)被收回,并使用所有交换。来完成对要写入的虚拟内存的备份。
while循环在犯什么错误?
一个错误,一个逻辑错误。
错误是,您不检查malloc()
是否返回NULL。当没有更多的虚拟内存可用(或内核出于任何原因拒绝提供更多内存)时,malloc()
返回NULL。 (在正常操作中,每个进程可用的虚拟内存仅限于非特权用户;请运行ulimit -a
来查看当前限制。)
由于分配后您立即访问内存,因此内核仅拒绝在RAM和SWAP用尽时允许您的进程更多,而malloc()
返回NULL。然后,您取消引用NULL指针(使用memset(NULL, 0, 1<<20)
),这会导致分段错误。
逻辑问题是该方案不会触发内核OOM杀手。
请记住,为了触发内核OOM杀手,您的进程必须分配尚未访问的内存。内核仅在已经提供虚拟内存时才调用OOM杀手,但无法将其与实际RAM一起备份,因为RAM中没有可收回的东西,并且交换已满。
在您的情况下,OOM杀手将不会被唤醒,因为当内核用尽RAM并进行交换时,它可以简单地拒绝提供更多(虚拟内存),从而导致malloc()
返回NULL。
(Linux内核内存子系统是一个积极开发的子系统,因此,您看到的确切行为取决于内核版本,RAM和交换的数量以及内存管理器可调参数(例如,{{3}下的可调参数) } ..以上介绍了最常见的或典型情况和配置。)
您也不需要外部阵列。例如,您可以将分配链接到链表:
#include <stdlib.h>
#include <stdio.h>
#ifndef SIZE
#define SIZE (2*1024*1024) /* 2 MiB */
#endif
struct list {
struct list *next;
size_t size;
char data[];
}
struct list *allocate_node(const size_t size)
{
struct list *new_node;
new_node = malloc(sizeof (struct list) + size);
if (!new_node)
return NULL;
new_node->next = NULL;
new_node->size = size;
}
int main(void)
{
size_t used = 0;
struct list *root = NULL, *curr;
/* Allocate as much memory as possible. */
while (1) {
curr = allocate_node(SIZE - sizeof (struct list));
if (!curr)
break;
/* Account for allocated total size */
used += SIZE;
/* Prepend to root list */
curr->next = root;
root = curr;
}
printf("Allocated %zu bytes.\n", used);
fflush(stdout);
/* Use all of the allocated memory. */
for (curr = root; curr != NULL; curr = curr->next)
if (curr->size > 0)
memset(curr->data, ~(unsigned char)0, curr->size);
printf("Wrote to %zu bytes of allocated memory. Done.\n", used);
fflush(stdout);
return EXIT_SUCCESS;
}
注意,上面的代码未经测试,甚至未经编译,但逻辑是合理的。如果您发现其中的错误或其他问题,请在评论中告知我,以便我进行验证和修复。