在分叉时执行外部程序是不可取的

时间:2015-07-16 10:54:05

标签: c linux posix

我有一个可以占用4-8GB内存的大型服务器软件。

这使得fork-exec很麻烦,因为fork本身可能需要很长时间,而且默认行为似乎是fork会失败,除非有足够的内存用于整个驻留内存的副本。

因为在开始分析时,我开始表现为最热门的地方(60%的时间花在分叉上),我需要解决它。

避免fork-exec例程的最简单方法是什么?

3 个答案:

答案 0 :(得分:6)

您基本上无法避免fork(2)(或等效的clone(2)系统调用...,或我不建议使用的过时vfork)+ execve(2)启动Linux和(可能)MacOSX以及大多数其他Unix或POSIX系统上的外部命令(àlasystem(3)或àlaposix_spawn)。

是什么让你认为它正在成为一个问题?今天8GB进程虚拟地址空间并不是什么大问题(至少在8Gbytes或16GB内存的机器上,就像我的桌面一样)。由于所有最近的Unix使用的懒惰copy-on-write技术,你实际上并不需要两倍的RAM(但你确实需要交换空间)。 Linux操作系统。

也许您可能认为交换空间可能是一个问题。在Linux上,您可以通过交换文件来添加交换空间;只是以root身份运行:

 dd if=/dev/zero of=/var/tmp/myswap bs=1M count=32768
 mkswap /var/tmp/myswap
 swapon /var/tmp/myswap

(当然,请确保/var/tmp/不是tmpfs挂载的文件系统,但是它位于某个磁盘上,可能是SSD磁盘....)

如果您不再需要大量的交换空间,请运行swapoff /var/tmp/myswap ....

您也可以在程序开头附近启动一些外部shell进程(àlapopen),稍后您可以向它发送shell命令。看看我的execicar.c程序获取灵感,或者如果它适合的话使用它(我10年前为了类似的目的写了它,但我忘记了细节)

或者在程序开头派一些解释器(LuaGuile ...)并向其发送一些命令。

每秒运行几十个命令(启动任何外部程序)不合理,应该被视为设计错误,恕我直言。也许正在运行的命令可以被进程内函数替换(例如/bin/ls可以使用statreaddirglob 函数 ...)。也许您可以考虑在代码中添加一些plugin能力(使用dlopen(3)& dlsym)(并从插件运行函数,而不是经常启动程式)。或者在您的代码中嵌入一个解释器(Lua,Guile,...)。

例如,对于Web服务器,请查找旧CGI vs FastCGI或HTTP转发(例如URL redirection)或嵌入式PHP或{{3 }}或HOP

答案 1 :(得分:2)

  

这使得fork-exec很麻烦,因为fork本身可以使用   重要的时间

这只是一半。您没有指定操作系统,但fork(2)在Linux中进行了相当优化(我相信其他UNIX变体),使用copy-on-write。写时复制意味着操作系统不会复制整个父内存地址空间,直到子(或父)写入内存。所以你可以放心,如果你有一个父进程使用8 GB内存然后你分叉,你将不会使用16 GB的内存 - 尤其是如果孩子立即执行()。

  除非有足够的内存用于整个副本,否则

fork将失败   居民记忆。

没有。 fork(2)引起的唯一开销是为子进程复制和分配任务结构,分配PID以及复制父进程的页表。如果没有足够的内存来复制整个父地址空间,fork(2)失败,如果没有足够的内存来分配新的任务结构和页表,它将失败。如果已达到用户的最大进程数,它也可能会失败。您可以在man 2 fork中确认这一点(注意:请参阅下面的评论)。

如果您仍然不想使用fork(2),则可以使用vfork(2),它根本不复制 - 它甚至不复制页面表 - 所有内容都与父级共享。您可以使用它来创建一个具有可忽略的开销的新子进程,然后执行exec()。请注意vfork(2)阻止调用线程,直到子进退或调用七个exec()函数之一。在调用任何exec()函数之前,也不应该修改子进程内的内存。

答案 2 :(得分:1)

您提到您每秒可以fork + exec 10k次。这听起来非常过分。你有没有考虑过让你exec成为守护进程的东西?或者可以在应用程序中实现这些外部程序?这听起来非常狡猾。

fork很可能开始失败,尽管有内存支持它,因为你的Linux已经禁用(或限制)内存过度使用。检查文件/proc/sys/vm/overcommit_memory。如果它是1然后我的猜测是错误的,还有其他奇怪的事情发生。如果它是0那么你根本不允许过度使用。如果它是2那么你需要阅读文档以确定如何配置它。

上面提到的一个解决方案就是添加交换(永远不会被使用)。

另一个解决方案是实现一个小守护进程,该守护进程将接受命令并执行那些forks和exec,以便为您提供所需的输出。

N.B。理论上,fork一个大型过程可以像一个小过程一样快。 fork的性能取决于你拥有多少内存映射,而不是它们覆盖的内存量。根据映射设置写时复制。除了在某些操作系统上设置匿名映射的COW与这些映射中的内存量呈线性关系,但我不知道Linux在这里做了什么,上次我在Linux上研究VM系统已超过15年了。