是否可以在没有继承父进程的虚拟内存空间的情况下派生进程?

tia*_*ozi 9 c linux fork enomem

由于父进程是使用的内存巨大的安装,fork可能会失败errnoENOMEM下仁过载策略的一些配置.即使子进程可能只有exec低内存消耗程序,如ls.

为了澄清问题,当/ proc/sys/vm/overcommit_memory配置为2时,(虚拟)内存的分配仅限于SWAP + MEMORY * ration(default to 50%).当一个进程分叉时,由于COW没有复制虚拟内存.但是内核仍然需要分配虚拟内存空间.作为类比,fork就像malloc(虚拟内存空间大小),它不会分配物理内存,写入共享内存会导致分配虚拟内存和物理内存的副本.当overcommit_memory配置为2时,由于虚拟内存空间分配,fork可能会失败.

fork在以下条件下,是否可以在没有父进程的继承虚拟内存空间的情况下进行处理?

  1. 如果子进程调用exec之后fork

  2. 如果子进程没有调用exec,则不会使用父进程中的任何全局或静态变量.例如,子进程只执行一些日志记录然后退出.

Bas*_*tch 7

不,这是不可能的.您可能对vfork(2)感兴趣,我不推荐.另请参阅mmap(2)及其MAP_NORESERVE标志.但是内核使用了写时复制技术,因此实际上不会使RAM消耗增加一倍.

我的建议是有足够的交换空间,不要担心这样的问题.因此,将计算机设置为具有比最大运行进程更多的可用交换空间.您总是可以创建一些临时交换文件(例如,dd if=/dev/zero of=/var/tmp/swapfile bs=1M count=32768然后使用mkswap /var/tmp/swapfile)然后将其添加为临时交换区域(swapon /var/tmp/swapfile)并在您不再需要它时将其删除(swapoff /var/tmp/swapfilerm /var/tmp/swapfile).

您可能不希望像往常一样交换tmpfs文件系统/tmp/,因为tmpfs文件系统由交换空间备份!

我不喜欢内存过度使用,我禁用它(通过proc(5)).因人而异.


Nik*_*lis 5

我不知道有任何方法可以做(2),但对于(1)你可以尝试使用vfork哪个将分叉一个新进程而不复制父进程的页表.但是出于多种原因通常不建议这样做,包括因为它导致父进程阻塞直到子进行execve或终止.


Nom*_*mal 5

正如Basile Starynkevitch 回答的那样,这是不可能的。

但是,有一个非常简单且通用的解决方案,它不依赖于Linux特定的行为或内存过量使用控制:使用早期分叉的从属进程执行fork和exec。

具有大父进程创建一个UNIX域套接字和fork一个从属进程尽早,关闭在从所有其它描述符(重新打开STDIN_FILENOSTDOUT_FILENOSTDERR_FILENO/dev/null)。尽管流套接字也可以工作,但我更喜欢数据报套接字的简单性和保证性。

在极少数情况下,使从属进程执行单独的专用小型帮助程序很有用。在大多数情况下,这不是必需的,这会使安全性设计变得更加容易。(在Linux中,当使用Unix域套接字传递数据时,可以包括SCM_CREDENTIALS辅助消息,并在其中使用进程ID来验证对等方正在使用/proc/PID/exe伪文件的身份/可执行文件。)

无论如何,从属进程将阻止从套接字读取。当另一端关闭套接字时,读/接收将返回0,并且从属进程将退出。

从属进程接收的每个数据报都描述了要执行的命令。(使用数据报可以使用以NUL字符定界的C字符串,而没有任何转义之类;使用Unix流套接字通常需要您以某种方式定界“命令”,这反过来又意味着在命令组件字符串中转义定界符。)

从属进程创建一个或多个管道,并派生一个子进程。该子进程关闭原始的Unix套接字,用相应的管道端替换标准流(关闭其他端),并执行所需的命令。我个人更喜欢在Linux中使用额外的close-on-exec套接字来检测成功执行。在错误情况下,将errno代码写入套接字,以便从属父级也可以可靠地检测到故障和确切原因。如果成功,则从属父级将关闭不必要的管道末端,并回复有关成功的原始过程,而其他管道末端将作为SCM_RIGHTS辅助数据。发送消息后,它将关闭管道的其余部分,并等待新消息。

在原始过程方面,上述过程是顺序的;一次只能执行一个线程以开始执行外部进程。(您只需使用互斥锁序列化访问权限即可。)可以同时运行多个访问权限。序列化的只是对从属助手的请求和响应。

如果这是一个问题(在典型情况下不应该这样),则可以例如通过为每个消息添加一个ID号(由父进程分配,单调递增)来对连接进行多路复用。在这种情况下,您可能会在父端使用专用线程来管理与从属设备的通信,因为您当然不能同时从同一个套接字读取多个线程,并且无法获得确定的结果。

该方案的进一步改进包括诸如为执行的进程使用专用进程组,设置对它们的限制(通过设置对从属进程的限制)以及通过使用特权从属以专用用户和组的身份执行命令。

在特权的从属情况下,让父级为其执行单独的帮助程序进程最为有用。在Linux中,双方都可以SCM_CREDENTIALS通过Unix域套接字使用辅助消息来验证对等方的身份(PID,并带有ID,可执行文件),从而可以轻松实现强大的安全性。(但请注意,/proc/PID/exe必须进行多次检查,以捕获由恶意程序发送消息的攻击,并迅速执行适当的程序,但带有导致其很快退出的命令行参数,使其偶尔看起来像正确的可执行文件发出了请求,而描述符的副本以及整个通信通道则由一个无关紧要的用户控制。)

总而言之,尽管提出的问题的答案为“否”,但仍然可以解决原始问题。如果执行对安全性敏感,例如更改特权(用户帐户)或功能(在Linux中),则必须谨慎设计考虑,但在正常情况下,实现是很简单的。

如有必要,我很乐意详细说明。