【问题标题】:fork() failing with Out of memory errorfork() 因内存不足错误而失败
【发布时间】:2013-03-14 13:16:18
【问题描述】:

父进程在尝试 fork 子进程时失败并显示 errno=12(内存不足)。父进程在 Linux 3.0 内核 - SLES 11 上运行。在分叉子进程时,父进程已经使用了大约 70% 的 RAM(180GB/256GB)。这个问题有解决办法吗?

应用程序是用 C++ 编写的,使用 g++ 4.6.3 编译。

【问题讨论】:

  • 我无法回答,因为我没有分叉的想法。需要一点信息
  • 您可能遇到操作系统限制,请参阅 setrlimit
  • 您真正应该问的问题是如何打印回溯,而不是如何使fork 工作;你没有告诉 - 除了最近的 cmets - 你正在分叉运行 gdb 然后它的 bt 命令!

标签: linux fork virtual-memory memory-overcommitment


【解决方案1】:

fork-ing 需要资源,因为它是copy-on-writing 进程的可写页面。再次阅读 fork(2) 手册页。

您至少可以提供一个巨大的临时交换文件。你可以(在一些有足够空间的文件系统上)创建一个巨大的文件$SWAPFILE

  dd if=/dev/zero of=$SWAPFILE bs=1M count=256000
  mkswap $SWAPFILE
  swapon $SWAPFILE

否则,您可以例如以不同的方式设计您的程序,例如mmap-ing 一些大文件(munmap-ing 在 fork 之前,mmap-ing 之后再次),或者更简单地从程序的开头开始一个 popen-ed shell,或p2open-ed 或明确地使pipe-s 往返于它(可能多路复用调用à la poll 也很有用),然后向它发出命令。

如果我们知道你的程序在做什么,为什么它会消耗这么多内存,以及它为什么和什么是分叉的,也许我们可以提供更多帮助......

阅读Advanced Linux Programming了解更多信息。

附言。

如果您 fork 只是为了运行 gdb 来显示回溯,请考虑使用更简单的替代方法,例如 recent GCC's libbacktraceWolf's libbacktrace...

【讨论】:

  • 我正在尝试调用非交互式 gdb 会话以获取完整的回溯。巨大的内存使用是由于应用程序的要求。这是意料之中的,而不是由于任何泄漏。
  • 启动 popen()ed shell 的建议似乎是个好主意,但是否可以启动双向管道进行读写?
  • 您也可以使用libbacktrace 或简单地运行gdb 与它的-p $pidnumber 参数交互。
【解决方案2】:

也许在您的系统中防止了虚拟内存过度提交。

如果被阻止,那么虚拟内存不能大于物理 RAM + 交换的大小。如果允许,那么虚拟内存可以大于 RAM+swap。

当您的进程分叉时,您的进程(父进程和子进程)将拥有 2*180GB 的虚拟内存(如果您没有交换空间,那就太多了)。

所以,通过这种方式允许过度提交:

 echo 1 > /proc/sys/vm/overcommit_memory

如果子进程立即执行,或者在父进程向自己的内存写入过多之前释放分配的内存,这应该会有所帮助。因此,请注意,如果两个进程都继续使用所有内存,内存不足杀手可能会起作用。

proc(5) 的手册页说:

/proc/sys/vm/overcommit_memory

此文件包含内核虚拟 内存计费模式。值为: 0:启发式过度使用(这是 默认值)1:总是过度使用,从不检查 2:总是检查,从不 过度使用

在模式 0 中,不检查带有 MAP_NORESERVE 的 mmap(2) 调用,并且 默认检查非常弱,导致有获得a的风险 进程“OOM-killed”。在 Linux 2.4 下,任何非零值都意味着模式 1. 在模式 2 中(自 Linux 2.6 起可用),系统上的总虚拟地址空间被限制为 (SS + RAM*(r/100)),其中 SS 是 交换空间的大小,RAM 是物理内存的大小, r 是文件 /proc/sys/vm/overcommit_ratio 的内容。

更多信息在这里:Overcommit Memory in SLES

【讨论】:

  • 这很有帮助,但可能对流程的正常运行造成危险后果。
  • @Basile Starynkevitch :是的,它不是那么安全。答案中添加了警告。
  • 确实很有帮助!确保您应该知道父母和孩子的实际内存使用情况。
【解决方案3】:

在 Linux 上一个更好的解决方案是使用 vfork 或 posix_spawn(因为它会尽可能使用 vfork): vfork“创建新进程而不复制父进程的页表”,所以即使你的应用程序使用超过 50% 的可用 RAM。

注意 std::system 和 QProcess::execute 也在底层使用 fork,在 Qt 框架中甚至有一张关于这个问题的票:https://bugreports.qt.io/browse/QTBUG-17331

【讨论】:

    猜你喜欢
    • 2014-01-06
    • 2021-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-28
    • 2016-06-02
    • 1970-01-01
    相关资源
    最近更新 更多