为什么使用`clone`创建进程会导致内存不足?

时间:2017-02-23 08:10:30

标签: unix memory process rust fork

我有一个在32GB机器上分配大约20GB RAM的进程。在一些事件之后,我将数据从父进程流式传输到子进程的stdin。在孩子出生时,必须在父进程中保留20GB的数据。

该应用程序是用Rust编写的,我正在调用Command::new('path/to/command')来创建子进程。

当我生成子进程时,操作系统正在捕获内存不足错误。

strace输出:

  

[pid 747] 16:04:41.128377 clone(child_stack = 0,flags = CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | SIGCHLD,child_tidptr = 0x7ff4c7f87b10)= -1 ENOMEM(无法分配内存)

陷阱为什么会发生?子进程的消耗不应超过1GB,exec()后会立即调用clone()

1 个答案:

答案 0 :(得分:3)

问题

当Rust调用创建子进程时,在C / C ++级别会发生一些事情。这是一种简化,但它有助于解释这一难题。

  1. 流重复(使用dup2或类似的呼叫)
  2. 父进程是分叉的(使用fork或clone系统调用)
  3. 分叉进程执行子进程(来自execvp系列的调用)
  4. 父和子现在是并发进程。您当前正在使用的Rust调用似乎是一个克隆调用,其行为非常类似于纯粹的fork,所以您不需要考虑操作系统所需的空间以及其他任何内容,因此您需要20G x 2 - 32G = 8G。可能正在运行。克隆调用返回负返回值,并且通过调用ENOMEM errno设置errno。

    如果在任何时候添加物理内存,压缩数据或通过不需要整个内存的流程将其流式传输的架构解决方案都不是选项,那么经典解决方案相当简单。

    <强>建议

    将父流程设计为精益流程。然后产生两个工人子,一个处理你的20GB需求,另一个处理你的1 GB需要 1 。这些孩子可以通过管道,文件,共享内存,套接字,信号量,信令和/或其他通信机制彼此连接,就像父母和孩子一样。

    从Apache httpd到嵌入式单元塔路由守护进程的许多成熟软件包都使用这种设计模式。它可靠,可维护,可扩展,便于携带。

    32G可能足以满足20G和1G处理需求,以及操作系统和精益父母流程。

    尽管此解决方案肯定会解决您的问题,但如果要在以后重用或扩展代码,则可能有必要研究涉及数据帧或多维切片的潜在流程设计更改,以支持数据流和内存需求减少。

    内存过度使用

    将overcommit_memory设置为1可消除问题中引用的克隆错误条件,因为Rust调用会调用读取该设置的LINUX克隆调用。但是这个解决方案有一些注意事项可以说上述建议是优越的,主要是1的值是危险的,特别是对于生产环境。

    <强>背景

    关于OpenBSD rfork和克隆调用的内核讨论随后发生在20世纪90年代末和21世纪初。这些讨论产生的特征允许比流程更少的极端分叉,这与pthreads之间提供更广泛的独立性是对称的。其中一些讨论产生了对已进入POSIX标准化的传统流程产生的扩展。

    在21世纪初期,Linux Torvalds提出了一种标志结构,用于确定执行模型的哪些组件共享以及在执行分支时复制的内容,从而模糊了进程和线程之间的区别。从此,克隆呼叫出现了。

    如果在这些线程中有任何内容,则不会过多地讨论过度提交内存。设计目标是更好地控制fork的结果,而不是将内存使用优化委派给操作系统启发式,这是overcommit_memory = 0的默认设置。

    <强>注意事项

    内存过量使用超出了这些扩展,增加了其模式权衡的复杂性 2 ,设计趋势警告 3 ,实际运行时限制 4 < / sup>,性能影响 5

    便携性和长寿

    此外,如果没有标准化,使用内存过量使用的代码可能不可移植,并且长寿的问题是相关的,尤其是当设置控制函数的行为时。如果设置系统发生变化,则无法保证向后兼容,甚至无法保证一些停机警告。

    <强>危险

    linuxdevcenter文档 2 表示,&#34; 1总是过度使用。也许你现在意识到这种模式的危险。&#34;,并且还有其他危险的迹象,总是过度使用 6,7

    LINUX,Windows和VMWare上的过度使用的实施者可能会保证可靠性,但它是一种统计游戏,结合过程控制的许多其他复杂性,在某些条件下可能会导致某些不稳定的特性。即使是名称过度使用,也会告诉我们它作为一种实践的真实性质。

    非默认的overcommit_memory模式,其中有几个警告是有问题的,但是立即尝试立即案例的工作可能会导致间歇性的可靠性。

    可预测性及其对系统可靠性和响应时间一致性的影响

    从贝尔实验室开始,类UNIX操作系统中的进程的想法是,进程向其容器(操作系统)发出具体请求。结果既可预测又是二进制。请求被拒绝或被授予。一旦被授予,该过程就可以完全控制并直接访问资源,直到该过程放弃使用它。

    虚拟内存的交换空间方面违反了这一原则,当RAM被大量消耗时,该原则在工作站上显示为活动的大幅减速。例如,在开发过程中有时候按下一个键并且必须等待十秒才能看到显示屏上的字符。

    <强>结论

    有很多方法可以充分利用物理内存,但希望分配的内存使用稀疏可能会带来负面影响。当过度使用过度使用时,交换性能会受到很好的记录。如果要在RAM中保留20G的数据,情况尤其如此。

    只分配所需内容,以智能方式分叉,使用线程,释放肯定不再需要的内存,从而在不影响可靠性的情况下节省内存,在交换磁盘使用中产生高峰,并且可以在不增加限制的情况下运行系统资源。

    Command::new调用的设计者的位置可能基于此观点。在这种情况下,在fork之后多久调用exec并不是产生期间请求内存量的决定因素。

    备注和参考

    [1]产卵工人的孩子可能需要一些代码重构,并且在肤浅的层面上看起来太麻烦,但重构可能会非常简单直接且显着有益。

    [2] http://www.linuxdevcenter.com/pub/a/linux/2006/11/30/linux-out-of-memory.html?page=2

    [3] https://www.etalabs.net/overcommit.html

    [4] http://www.gabesvirtualworld.com/memory-overcommit-in-production-yes-yes-yes/

    [5] https://labs.vmware.com/vmtj/memory-overcommitment-in-the-esx-server

    [6] https://github.com/kubernetes/kubernetes/issues/14452

    [7] http://linuxtoolkit.blogspot.com/2011_08_01_archive.html