使用 vfork() 和 -lpthread 的孙子的 getpid() 结果不正确

Incorrect result from getpid() for grandchild with vfork() and -lpthread

在下面显示的一种特殊情况下,getpid() 对于使用 vfork() returns 父进程的 PID 创建的孙进程。

#include <stdio.h>
#include <stdlib.h>

int main() {
  if(vfork()) { /* parent */
    printf("parent pid = %d\n", getpid());
    exit(0);
  } else {
    if(vfork()) { /* child */
      printf("child pid = %d\n", getpid());
      exit(0);
    } else { /* grandchild */
      printf("grandchild pid = %d\n", 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 不是允许您在 child 中的 vfork 之后执行的两个操作之一;仅有的两个是 execve_exit。碰巧 glibc 将进程的 pid 缓存在用户空间中,并且不会在 vfork 上更新此缓存(因为它会修改 parent 的缓存值,并且由于有效代码不能观察结果);这就是您所看到的行为的机制。 -lpthread 链接的缓存行为略有不同。但根本原因是您的代码无效。

差不多,不要用vfork。基本上您无能为力。

来自 the manual page for vfork():

The vfork() function has the same effect as fork(2), except that the behavior is undefined if the process created by vfork() either modifies any data other than a variable of type pid_t used to store the return value from vfork(), or returns from the function in which vfork() was called, or calls any other function before successfully calling _exit(2) or one of the exec(3) family of functions.

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

  • 检查 return 值。
  • 调用 exec*() 函数族之一。
  • 呼叫_exit().

这是因为:

vfork() is a special case of clone(2). It is used to create new processes without copying the page tables of the parent process. It may be useful in performance-sensitive applications where a child is created which then immediately issues an execve(2).

换句话说,vfork() 的预期用途只是创建 children,它将通过 exec*() 执行其他程序,这比正常的 fork() 因为 parent 的页面 table 在 child 中没有重复(因为它无论如何都会被 exec*() 替换)。尽管如此,vfork() 只有在这种操作需要多次执行时才有真正的优势。由于 parent 内存未被复制,因此以任何方式访问它都是未定义的行为。

这是vfork()

的要求
   #include <sys/types.h>
   #include <unistd.h>

   pid_t vfork(void);

请注意 OP 发布的代码未能包含所需的头文件。