C语言 getpid() 对带有 vfork() 和 -lpthread 的孙子的错误结果



在下面显示的特殊情况之一中,vfork()创建的孙子getpid()返回父进程的PID。

#include <stdio.h>
#include <stdlib.h>
int main() {
if(vfork()) { /* parent */
printf("parent pid = %dn", getpid());
exit(0);
} else {
if(vfork()) { /* child */
printf("child pid = %dn", getpid());
exit(0);
} else { /* grandchild */
printf("grandchild pid = %dn", getpid());
exit(0);
}
}
}

编译为gcc main.c,这按预期工作:

grandchild pid = 12241
child  pid = 12240
parent pid = 12239

编译为gcc main.c -lpthread,孙子PID不正确:

grandchild pid = 12431
child pid = 12432
parent pid = 12431

有什么线索吗?这是未定义的行为案例之一吗?

通过psstrace,我可以看到正确的PID。顺便说一句,相同的示例代码适用于fork(),即带或不带-lpthread的正确getpid()

getpid

不是孩子vfork后允许执行的两个操作之一;只有两个是execve_exit。碰巧 glibc 在用户空间中缓存进程的 pid,并且不会在vfork上更新此缓存(因为它会修改父级的缓存值,并且由于有效代码无法观察结果,因此不需要它(;这就是你所看到的行为的机制。缓存行为与链接-lpthread略有不同。但根本原因是您的代码无效。

差不多,不要使用vfork.基本上你无能为力。

从手册页vfork()

vfork()函数与fork(2)具有相同的效果,只是如果 由vfork()创建的进程修改除用于存储返回值的pid_t类型的变量以外的任何数据 fromvfork(),或从调用vfork()的函数返回,或在成功之前调用任何其他函数 调用_exit(2)exec(3)函数系列之一。

它的措辞不是很好,但这是在说子进程在vfork()之后唯一可以做的事情是:

  • 检查返回值。
  • 调用exec*()函数系列之一。
  • 呼叫_exit()

这是因为:

vfork()clone(2)的特例。 它用于创建新进程,而无需复制父进程的页表。 它可能在性能敏感的应用程序中很有用,在这些应用程序中,创建一个子项,然后立即 发出execve(2)

换句话说,vfork()的预期用途只是创建将通过exec*()执行其他程序的子项,使其比普通fork()更快,因为父项的页表在子项中没有重复(因为它无论如何都会被exec*()替换(。即便如此,只有当这种操作需要多次执行时,vfork()才具有真正的优势。由于父内存未被复制,因此以任何方式访问它都是未定义的行为。

这里是vfork()的要求

#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);

请注意,OP 发布的代码无法包含所需的头文件。

相关内容

  • 没有找到相关文章

最新更新