在由gcc编译的两个程序(版本5.4.0)上运行D1(版本5.4.0),一个调用vfork(),另一个调用fork()之后,我发现vfork()调用SYS_vfork,而fork()调用SYS_clone. 时,在任何地方都找不到有关这个特定行为的任何信息(一些来源说,每个fork()、vfork()和clone()都由相应的sys_调用实现,而其他来源则说所有三个调用都是使用sys_clone实现的)。
源代码:
#include
main()
{
int pid;
pid=vfork();
}来自ltrace -S的输出:
...
__libc_start_main([some stuff here]
vfork([more stuff]
SYS_vfork([more stuff])
--- SIGCHLD (Child exited) ---为什么libc在vfork()中使用SYS_vfork,而不使用SYS_fork来实现fork()呢?我读过托马斯·尼曼的回答给内核中的哪个文件指定叉(),vfork()…使用sys_克隆()系统调用的文章,其中说:
然后,
vfork()通过一个单独的CLONE_VFORK标志实现,这将导致父进程休眠,直到子进程通过信号唤醒它。子线程将是父名称空间中唯一的执行线程,直到它调用exec()或退出为止。不允许孩子在记忆中写字。相应的clone()调用如下:clone(CLONE_VFORK | CLONE_VM | SIGCHLD, 0)
这似乎与我观察到的ltrace -S输出不一致。
是我搞砸了什么,还是滑头编写人员故意选择使用vfork()而不是SYS_clone来实现?这被认为是在任何时候都可以改变的东西,还是我们可以依赖的东西?
发布于 2018-03-26 22:59:37
是我搞砸了什么,还是glibc作者故意选择使用SYS_vfork而不是SYS_clone来实现vfork()是有原因的?
从历史上看,我认为这更有可能是vfork不需要更改的结果。vfork和fork最初都使用等效的系统调用。当NPTL线程被实现时,这个fork实现改为使用clone,因为C库需要重置线程id.。vfork不需要担心线程,因为它只适合与execve一起使用(这无论如何都会重置所有的状态),所以它没有受到任何影响。
NPTL设计论文解释了为什么当线程易于使用时,fork系统调用不足以实现fork库调用:
要在没有内存泄漏的情况下实现
fork函数,除了调用fork的线程之外,用于堆栈和所有线程的其他内部信息的内存必须被回收。在这种情况下,内核是无能为力的。这被认为是在任何时候都可以改变的东西,还是我们可以依赖的东西?
由于您使用C库来分叉,所以只能依赖C库提供API中记录的行为;您不能依赖特定的实现。您不应该依赖vfork(3)使用vfork(2)系统调用而不是clone(2),也不应该依赖使用clone(2)而不是fork(2)的fork(3)。请注意,所使用的系统调用可能因体系结构而异.
如果您确实需要依赖特定的系统调用,则应该直接使用这些调用,并放弃C库包装器。
发布于 2018-03-26 22:50:57
man 2 fork:
自2.3.3版本以来,C库/内核之间的差异,而不是调用内核的叉()系统调用,而是作为NPTL线程实现的一部分提供的glibc叉()包装器调用具有与传统系统调用相同效果的标志的克隆(2)。(对fork()的调用等价于对克隆(2)的调用,将标志指定为仅为SIGCHLD)。glibc包装器调用使用pthread_atfork(3)建立的任何分叉处理程序。
https://unix.stackexchange.com/questions/433702
复制相似问题