Unix 为什么使用“克隆”创建进程会导致内存不足故障?

Unix 为什么使用“克隆”创建进程会导致内存不足故障?,unix,memory,process,rust,fork,Unix,Memory,Process,Rust,Fork,我有一个进程,在32GB的机器上分配大约20GB的RAM。在一些事件之后,我将数据从父进程流式传输到子进程的stdin。在生成子进程时,必须在父进程中保留20GB的数据 这个应用程序是用Rust编写的,我正在调用Command::new('path/to/Command')来创建子进程 生成子进程时,操作系统捕获内存不足错误 strace输出: [pid 747]16:04:41.128377克隆(child_stack=0,flags=clone_child_CLEARTID | clone_

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

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

生成子进程时,操作系统捕获内存不足错误

strace输出:

[pid 747]16:04:41.128377克隆(child_stack=0,flags=clone_child_CLEARTID | clone_child_SETTID | SIGCHLD,child_tidptr=0x7ff4c7f87b10)=-1个ENOMEM(无法分配内存)


为什么会出现陷阱?子进程的消耗量不应超过1GB,并且在
clone()
之后立即调用
exec()

问题

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

  • 流被复制(使用dup2或类似调用)
  • 父进程已分叉(使用分叉或克隆系统调用)
  • 分叉进程执行子进程(通过execvp家族的调用)
  • 父进程和子进程现在是并发进程。您当前使用的Rust调用似乎是一个克隆调用,其行为非常类似于纯fork,因此您的长度为20G x 2-32G=8G,而没有考虑操作系统所需的空间以及可能正在运行的任何其他内容。克隆调用返回的返回值为负值,并且通过调用ENOMEM errno设置errno

    如果添加物理内存、压缩数据或流式传输数据的体系结构解决方案在任何时候都不需要全部存储在内存中,那么经典解决方案就相当简单

    建议

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

    从apachehttpd到嵌入式cell-tower路由守护进程的许多成熟软件包都使用这种设计模式。它是可靠的、可维护的、可扩展的和可移植的

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

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

    内存总是过多

    将Overmit_memory设置为1可消除问题中引用的克隆错误条件,因为Rust调用读取该设置的LINUX克隆调用。但是,此解决方案有几个警告,可以追溯到上述建议的优越性,主要是1的值是危险的,特别是对于生产环境

    背景

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

    在21世纪初,Linux Torvalds提出了一种标志结构,用于确定共享执行模型的哪些组件,以及在执行分叉时复制哪些组件,从而模糊了进程和线程之间的区别。由此,克隆电话应运而生

    在这些线程中,过度提交内存的问题讨论得很少。设计目标是更多地控制fork的结果,而不是将内存使用优化委托给操作系统启发式,这是默认设置
    overmit_memory=0
    所做的

    注意事项

    内存过度使用超出了这些扩展,增加了其模式2、设计趋势说明3、实际运行时间限制4和性能影响5之间权衡的复杂性

    便携性和使用寿命

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

    危险

    linuxdevcenter文档2说,“1总是过度承诺。也许您现在意识到了此模式的危险性。”,并且总是过度承诺6、7还有其他危险迹象

    LINUX、Windows和VMWare上的Overmit实现者可以保证可靠性,但这是一个统计游戏,再加上进程控制的许多其他复杂性,在某些情况下可能会导致某些不稳定的特性。就连Overmit这个名字也告诉我们它作为一种实践的真实特征

    非默认的Overmit_内存模式,在该模式下会出现多个警告,但适用于即时案例的即时审判,可能会导致间歇性可靠性

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

    从贝尔实验室开始,在类UNIX操作系统中使用进程的想法是,一个进程可以生成一个具体的re