clinuxforkvfork

Can a fork child determine whether it is a fork or a vfork?


Within the child process, is there any way that it determine whether it was launched as a fork with overlay memory, or a vfork with shared memory?

Basically, our logging engine needs to be much more careful (and not log some classes of activity) in vfork. In fork it needs to cooperate with the parent process in ways that it doesn't in vfork. We know how to do those two things, but not how to decide.

I know I could probably intercept the fork/vfork/clone calls, and store the fork/vfork/mapping status as a flag, but it would make life somewhat simpler if there was an API call the child could make to determine its own state.

Extra marks: Ideally I also need to pick up any places in libraries that have done a fork or vfork and then called back into our code. And how that can happen? At least one of the libraries we have offers a popen-like API where a client call-back is called from the fork child before the exec. Clearly the utility of that call-back is significantly restricted in vfork.


Solution

  • A simple solution could use pthread_atfork(). The callbacks registered with this service are triggered only upon fork(). So, the 3rd parameter of the function, which is called in the child process right after the fork, could update a global variable. The child can check the variable and if it is modified, then it has been forked:

    /*
      Simple program which demonstrates a solution to
      make the child process know if it has been forked or vforked
    */
    #include <pthread.h>
    #include <sys/wait.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    pid_t forked;
    
    void child_hdl(void)
    {
      forked = getpid();
    }
    
    
    int main(void)
    {
    pid_t pid;
    
      pthread_atfork(0, 0, child_hdl);
    
      pid = fork();
      if (pid == 0) {
        if (forked != 0) {
          printf("1. It is a fork()\n");
        }
        exit(0);
      }
    
      // Father continues here
      wait(NULL);
    
      pid = vfork();
      if (pid == 0) {
        if (forked != 0) {
          printf("2. It is a fork()\n");
        }
        _exit(0);
      }
    
      // Father continues here
      wait(NULL);
    
      return 0;
    }
    

    Build/execution:

    $ gcc fork_or_vfork.c
    $ ./a.out
    1. It is a fork()