clinuxmultiprocessingforkvfork

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


In one of the special cases shown below, getpid() for the grandchild created with vfork() returns the PID of the parent process.

#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);
    }
  }
}

Compiled as gcc main.c, this works as expected:

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

Compiled as gcc main.c -lpthread, the grandchild PID is incorrect:

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

Any clues why? Is this one of the undefined behavior cases?

With ps and strace, I can see the correct PID. BTW, the same example code works fine with fork(), i.e. correct getpid() with or without -lpthread.


Solution

  • getpid is not one of the two operations you're permitted to perform after vfork in the child; the only two are execve and _exit. It happens that glibc caches the process's pid in userspace, and does not update this cache on vfork (since it would modify the parent's cached value, and since it's not needed since valid code can't observe the result); that's the mechanism of the behavior you're seeing. The caching behavior differs slightly with -lpthread linked. But the underlying reason is that your code is invalid.

    Pretty much, don't use vfork. There's basically nothing you can do with it.