c++ 访问大型数组时“被杀”[重复]

wwwo4jvm  于 2023-08-09  发布在  其他
关注(0)|答案(1)|浏览(110)

此问题在此处已有答案

How to get available memory C++/g++?(10个答案)
7天前关闭
截至6天前,社区正在审查是否重新讨论这个问题。
为什么我运行这个程序时会被“杀死”?

#include <stddef.h>

int main() {
    constexpr size_t N{5'000'000'000};
    unsigned int *bigdata;
    bigdata = new unsigned int[N];
    for (size_t i=0; i<N; i++) 
        bigdata[i] = i;
    return 0;
}

字符串
N设置为更大的值(如50'000' 000 '000)将生成std::bad_alloc。使用较小的值(如1'000'000' 000),程序工作正常。但是对于5'000'000' 000,它在写入数组时会非常不可预测地失败,突然在控制台上打印“Killed”并退出代码137。有没有办法知道在给定的系统上使用多大的数组是安全的?

附加信息

有人将这个问题标记为多余,链接到How to get available memory C++/g++?。这不是不可想象的,这个问题已经回答了,但链接的页面不是它。使用最高评级答案中提供的代码示例仍然会生成相同的错误。以下是完整的程序:

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

unsigned long long getTotalSystemMemory() {
  long pages = sysconf(_SC_PHYS_PAGES);
  long page_size = sysconf(_SC_PAGE_SIZE);
  return pages * page_size;
}

int main() {

  size_t N = getTotalSystemMemory();
  printf("Size: %zu \n", N);

  unsigned int *bigdata = (unsigned int *)malloc(N);
  for (size_t i = 0; i < N / sizeof(unsigned int) - 1; i++)
    bigdata[i] = i;
  return 0;
}


输出:

Size: 16'439'369'728       // Separators added manually 
Killed

kh212irz

kh212irz1#

  • Killed* 和退出代码137(== 128 + 9 == 128 + SIGKILL)都对应于SIGKILL信号,它总是致命的(进程不可能忽略或处理)。

Linux内核向进程发送SIGKILL的一个原因是,如果进程使用了太多内存,它将终止该进程。更具体地,这些是一些可能的情况:

  • 进程尝试写入以前未Map的虚拟内存页,但系统中没有可用页。只有在虚拟页被Map到物理存储器页之后才可能进行写入,但是此时没有这样的物理存储器页空闲。这种情况是可能的,因为 overcommit(另见问题下面的评论)。当内存分配给全局变量时,使用 mmapmalloc(具有巨大的大小),相应的数据页尚未Map。读取数据并返回零是可能的,但要写入数据,必须Map页面。此Map发生在第一次写入时。如果没有空闲的物理页,内核会向进程发送一个SIGKILL信号,终止进程。

有趣的是,man page mmap(2)文档中的SIGSEGV而不是SIGKILL。
man proc(5)中搜索 /proc/sys/vm/overcommit_memory,以获取有关如何在Linux上禁用或配置过度提交的文档。

  • Linux也有OOM killer,如果系统内存不足,它会向某些进程发送SIGKILL。它的React有多快,是否符合你的观察,我不知道。

要测试它,请运行您的进程,并在进程收到SIGKILL后快速运行dmesgdmesg内核日志输出中应该会显示一些OOM杀手消息。要进一步测试它,运行sudo sysctl -w vm.oom-kill = 0禁用OOM杀手,再次运行您的进程,如果SIGKILL没有发生,那么它就是OOM杀手(在禁用之前)。

相关问题