当我调用vfork()时,我可以调用任何exec*()函数,还是必须调用execve()?

Cor*_*lks 6 c linux exec vfork

从Linux手册页:

vfork()函数具有相同的效果fork(2),除非在成功调用[...] exec(3)函数族之一之前,如果进程调用任何其他函数,则行为未定义.

这表明exec*()之后调用任何函数vfork()是可以接受的.但是,稍后在手册页中明确说明:

特别是,程序员不能依赖父母保持阻止,直到孩子打电话execve(2)[...].

execve(2)在手册页中重复使用,它的用法表明它是唯一exec可以接受的类型函数vfork().

那么为什么execve被挑选出来,我可以安全地调用其他exec类型的函数(比如execlp)?

R..*_*R.. 5

你必须打电话execve.无法保证任何其他exec-family函数不会执行之后不安全的操作vfork.例如:

  • execl可以为参数列表分配内存.它必须是异步信号安全的,这意味着它不太可能使用malloc,但即使它没有,也没有办法在底层execve发生之后释放分配的内存(存在于父代的内存空间中),所以除非它设法在堆栈上构建参数列表,否则它(最多)会泄漏父节点中的内存.

  • execvp需要访问环境以执行路径搜索,还需要构造要传递到的连接路径名execve.后者可能需要分配,而前者可能会做各种不安全的vfork事情(注意:execvp甚至不是异步信号安全的).

等等

真的,你应该根本不使用vfork.几乎不可能安全使用它.特别是在使用信号处理程序的任何程序中都不安全,因为除非你阻塞所有信号,否则信号处理程序可以在子程序中共享父项的内存时运行(在这种情况下,子项将在exec之后继承一个完全阻塞的信号掩码,几乎肯定不是你想要的).

如果您正在寻找更有效的替代品fork,请使用posix_spawn.


oua*_*uah 1

在 Linux 上,所有exec*函数实际上都是系统调用之上的包装库函数execve。因此,通过调用,execlp您实际上也在调用execve

  • 如果库包装器分配动态内存怎么办?(例如构建数组。) (3认同)
  • @Cornstalks 只有 `execve` 位于 Linux 手册页的第 2 节(系统调用)中,其他 `exec*` 函数位于第 3 节(库调用)中。 (2认同)
  • @ouah:在`fork`之后,图像被替换,所以一切都很好(大多数情况下,尽管多线程分配器存在问题)。但使用“vfork”时,在“execve”系统调用之前,您仍然位于父内存中,因此内存会泄漏。不知何故,您希望保证库包装器不会“执行”任何操作。 (2认同)