在运行ltrace -S
由(版本 5.4.0)编译的两个程序gcc
(一个调用vfork()
另一个调用 )后fork()
,我发现vfork()
调用SYS_vfork
同时fork()
调用SYS_clone
。我在任何地方都找不到有关此特定行为的任何信息(一些消息来源说每个fork()
,vfork()
和clone()
都是由相应命名的sys_
调用实现的,而其他消息来源则说所有三个调用都是使用 实现的sys_clone
)。
源代码:
#include<stdio.h>
main()
{
int pid;
pid=vfork();
}
输出来自ltrace -S
:
...
__libc_start_main([some stuff here] <unfinished ...>
vfork([more stuff] <unfinished ...>
SYS_vfork([more stuff])
--- SIGCHLD (Child exited) ---
SYS_vfork
libc 使用forvfork()
但不使用SYS_fork
for是否有原因fork()
?我读了托马斯·尼曼的回答到
内核中的哪个文件指定fork()、vfork()…使用sys_clone()系统调用,其中说:
vfork()
反过来是通过一个单独的CLONE_VFORK
标志实现的,这将导致父进程休眠,直到子进程通过信号唤醒它。子级将是父级命名空间中唯一的执行线程,直到它调用exec()
或退出。不允许孩子写入内存。相应的clone()
调用可以如下:
clone(CLONE_VFORK | CLONE_VM | SIGCHLD, 0)
这似乎与我观察到的输出不一致ltrace -S
。
是我搞砸了一些东西还是 glibc 的作者故意选择使用vfork()
usingSYS_vfork
而不是SYS_clone
出于某种原因?这是否被认为是随时可能改变的事情,或者我们可以依赖它吗?
答案1
是我搞砸了,还是 glibc 编写者出于某种原因故意选择使用 SYS_vfork 而不是 SYS_clone 来实现 vfork() ?
从历史上看,我认为这更有可能只是vfork
不需要改变的结果。vfork
和最初都fork
使用等效的系统调用。当实现NPTL线程时,实施fork
更改为使用clone
, 因为C库需要重置线程id。vfork
不需要担心线程,因为它仅用于使用execve
(无论如何都会重置所有状态),因此它保持不变。
NPTL设计文件解释了为什么当线程可能被使用时fork
系统调用不足以实现库调用:fork
为了实现没有内存泄漏的功能,需要回收
fork
除线程调用之外的所有线程的堆栈和其他内部信息所使用的内存。fork
内核在这种情况下无能为力。
这是否被认为是随时可能改变的事情,或者我们可以依赖它吗?
由于您使用 C 库进行 fork,因此您只能依赖 C 库提供 API 中记录的行为;您不能依赖特定的实现。您不应该依赖于vfork(3)
使用vfork(2)
系统调用而不是clone(2)
,也不应该依赖于fork(3)
使用clone(2)
而不是fork(2)
。请注意,所使用的系统调用可能因一种体系结构而异......
如果您确实需要依赖特定的系统调用,则应该直接使用它们并放弃 C 库包装器。
答案2
C 库/内核差异
从版本 2.3.3 开始,作为 NPTL 线程实现的一部分提供的 glibc fork() 包装器不调用内核的 fork() 系统调用,而是使用提供与传统系统调用相同效果的标志来调用 clone(2) 。 (对 fork() 的调用相当于对 clone(2) 的调用,将标志指定为 SIGCHLD。)glibc 包装器调用使用 pthread_atfork(3) 建立的任何 fork 处理程序。