About vfork() system call?

1.2k views Asked by At
#include <stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>

int main()
{
   pid_t child_pid = vfork();

   if(child_pid < 0)
   {
       printf("vfork() error\n");
       exit(-1);
   }
   if(child_pid != 0)
   {
       printf("Hey I am parent %d\nMy child is %d\n",getpid(),child_pid);
       wait(NULL);
   }

   else
   {
       printf("Hey I am child %d\nMy parent is %d\n",getpid(),getppid());
       execl("/bin/echo","echo","hello",NULL);
       exit(0);
   }
   return 0;
}

output:

 Hey I am child 4
 My parent is 3
 Hey I am parent 3
 My child is 4
 hello

My question : Why is "hello" printed after the execution of parent process? I have started learning about vfork(). Could anyone please help me with this?

2

There are 2 answers

1
Peter Haddad On

After the parent process is executed, it goes to wait(NULL); which blocks the parent process until the child process calls execl() or exit.

Therefore the child process when the parent is blocked is calling the execl() and thus hello is being printed in the end of the output.

#include <stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>

int main()
{
pid_t child_pid = vfork();

if(child_pid < 0)
{
   printf("vfork() error\n");
   exit(-1);
}
if(child_pid != 0)
{
   printf("Hey I am parent %d\nMy child is %d\n",getpid(),child_pid);
   wait(NULL);
 }

 else
 {
   printf("Hey I am child %d\nMy parent is %d\n",getpid(),getppid());
   execl("/bin/echo","echo","hello",NULL);
   exit(0);
  }
 return 0;
  }

If you remove execl() the child process will go to the exit(0) and hello wont be printed.

Also, after execl() is being executed it is creating a new process thus any code you write after execl() wont be executed. According to the man page:-

The exec() family of functions replaces the current process image with a new process image. The functions described in this manual page are front-ends for execve(2). (See the manual page for execve(2) for further details about the replacement of the current process image.)

3
AudioBubble On

A piece of advice first: Don't use vfork(). On a modern system, the advantages of using vfork() over fork() are tiny. The code you show can never work properly with vfork() because it invokes undefined behavior:

POSIX.1:

The vfork() function has the same effect as fork(), 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() or one of the exec() family of functions.

So by calling printf() in your child code, your code is already undefined. Note that even a call to exit() would lead to undefined behavior, only _exit() is allowed.


I assume you try this on Linux, which defines the behavior of vfork() a bit more and in a way that explains what you observe:

From the linux vfork() manual page:

vfork() differs from fork(2) in that the calling thread is suspended until the child terminates (either normally, by calling _exit(2), or abnormally, after delivery of a fatal signal), or it makes a call to execve(2). Until that point, the child shares all memory with its parent, including the stack.

So on Linux, you can be sure that the child created by vfork() is executed first and only by your call to execl() (which calls execve() internally), the parent process is allowed to continue running. That's the reason you see the parent's output after your child's output. Once the parent calls wait(), it waits until the child finishes -- at this point, the child is replaced by echo.

Relying on this behavior makes your program non-portable to different implementations of vfork(). The call to printf() is fine because Linux suspends the parent process, and the wrong call to exit() doesn't matter here by luck: anything after an exec*() call is unreachable anyways (those functions never return, they replace the running program)!

Because of its weird semantics and the huge risk of bugs, vfork() was removed from POSIX in POSIX.1-2008. A safe and modern POSIX replacement for the typical usecase of a vfork() directly followed by a exec*() is posix_spawn().


All in all, you really shouldn't use vfork(). Use fork() instead. Remove the unreachable call to exit() as well and your program looks fine.