vfork() 调用 SYS_vfork 但 fork() 调用 SYS_clone?

vfork() 调用 SYS_vfork 但 fork() 调用 SYS_clone?

在运行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_vforklibc 使用forvfork() 但不使用SYS_forkfor是否有原因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库需要重置线程idvfork不需要担心线程,因为它仅用于使用execve(无论如何都会重置所有状态),因此它保持不变。

NPTL设计文件解释了为什么当线程可能被使用时fork系统调用不足以实现库调用:fork

为了实现没有内存泄漏的功能,需要回收fork除线程调用之外的所有线程的堆栈和其他内部信息所使用的内存。fork内核在这种情况下无能为力。


这是否被认为是随时可能改变的事情,或者我们可以依赖它吗?

由于您使用 C 库进行 fork,因此您只能依赖 C 库提供 API 中记录的行为;您不能依赖特定的实现。您不应该依赖于vfork(3)使用vfork(2)系统调用而不是clone(2),也不应该依赖于fork(3)使用clone(2)而不是fork(2)。请注意,所使用的系统调用可能因一种体系结构而异......

如果您确实需要依赖特定的系统调用,则应该直接使用它们并放弃 C 库包装器。

答案2

man 2 fork:

C 库/内核差异
从版本 2.3.3 开始,作为 NPTL 线程实现的一部分提供的 glibc fork() 包装器不调用内核的 fork() 系统调用,而是使用提供与传统系统调用相同效果的标志来调用 clone(2) 。 (对 fork() 的调用相当于对 clone(2) 的调用,将标志指定为 SIGCHLD。)glibc 包装器调用使用 pthread_atfork(3) 建立的任何 fork 处理程序。

相关内容