3

As far as I know, when vfork is called, the child process uses the same address space as that of the parent and any changes made by the child process in parent'ss variables are reflected onto the parent process. My questions are:

  • When a child process is spawned, is the parent process suspended?
  • If yes, why?
  • They can run in parallel (like threads)? After all, both threads and process call the same clone() function.

After bit of research and Googling, I found out that the parent process is not really suspended, but the calling thread is suspended. Even if this is the case, when the child process does an exit() or exec(), how does the parent process know that the child has exited? And what will happen if we return from a child process?

4 Answers 4

2

Your question is partly based on bad naming convention. A "thread of control" in kernel-speak is a process in user-speak. So when you read that vfork "the calling thread is suspended" think "process" (or "heavyweight thread" if you like) not "thread" as in "multi-threaded process".

  • So yes, the parent process is suspended.

vfork semantics were defined for the very common case where a process (the shell most often) would fork, mess with some file descriptors, and then exec another process in place. The kernel folks realized they could save a huge amount of page copying overhead if they skipped the copy since the exec was just going to throw those copied pages away. A vforked child does have its own file descriptor table in the kernel, so manipulating that doesn't affect the parent process, keeping the semantics of fork unchanged.

  • Why? Because fork/exec was common, expensive, and wasteful

Given the more accurate definition of "kernel thread of control", the answer to can they run in parallel is clearly

  • No, the parent will be blocked by the kernel until the child exits or execs

How does the parent know the child has exited?

  • It doesn't, the kernel knows and keeps the parent from getting any CPU at all until the child has gone away.

As for the last question, I would suspect that the kernel would detect the child stack operations involved in a return and signal the child with an uncatchable signal or just kill it, but I don't know the details.

2
  • 1
    are you sure about this? I've written quite simple program (yet too long to paste here) which creates 40 pthreads, performs vfork in each of them, and exec if pid=0 in each child process, performing 2x sleep(1) between vfork and exec, and logging each step. I can see in logs, that all vforks are batched together, then come all "midsteps" and then all execs. The only explanation I see to this is that other pthreads are not paused when vfork is called.
    – qbolec
    Oct 23, 2014 at 10:57
  • Yes, it is correct. Your test code contains actions that you don't fully understand, in particular how sleep() is implemented.
    – msw
    Oct 23, 2014 at 16:41
1

I am not a linux programmer, but facing the same question today, I've made a following test:

#include<unistd.h>
#include<signal.h>
#include<errno.h>
#include<fcntl.h>
#include<cassert>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<iostream>
#include<fstream>
#include<sstream>
#include<list>
using namespace std;
int single_talk(int thread_id){
  fprintf(stderr,"thread %d before fork @%d\n",thread_id,time(0));
  int pid=vfork();
  if(-1==pid){
    cerr << "failed to fork: " << strerror(errno) << endl;
    _exit(-3);//serious problem, can not proceed
  }
  sleep(1);
  fprintf(stderr,"thread %d fork returned %d @%d\n",thread_id,pid,time(0));
  if(pid){//"CPP"
    fprintf(stderr,"thread %d in parent\n",thread_id);
  }else{//"PHP"
    sleep(1);
    fprintf(stderr,"thread %d in child @%d\n",thread_id,time(0));
    if(-1 == execlp("/bin/ls","ls",(char*)NULL)){
      cerr << "failed to execl php : " << strerror(errno) << endl;
      _exit(-4);//serious problem, can not proceed
    }
  }
}
void * talker(void * id){
  single_talk(*(int*)id);
  return NULL;
}
int main(){
  signal(SIGPIPE,SIG_IGN);
  signal(SIGCHLD,SIG_IGN);
  const int thread_count = 44;
  pthread_t thread[thread_count];
  int thread_id[thread_count];
  int err;
  for(size_t i=0;i<thread_count;++i){
    thread_id[i]=i;
    if((err = pthread_create(thread+i,NULL,talker,thread_id+i))){
      cerr << "failed to create pthread: " << strerror(err) << endl;
      exit(-7);
    }
  }
  for(size_t i=0;i<thread_count;++i){
    if((err = pthread_join(thread[i],NULL))){
      cerr << "failed to join pthread: " << strerror(err) << endl;
      exit(-17);
    }
  }
}

I've compiled it with g++ -pthread -o repro repro.cpp and run with ./repro. What I see in the output is that everything happens simultanously in rounds : first all pthreads run vfork, then all wait a second, then "wake up" in the child process reality, then all childs run exec(), then finally all parents wake up.

To me, this proves that if one pthread calls a vfork it does not suspend the other pthreads - if it did, then they wouldn't be able to call vfork()s until the exec() is called.

4
  • What this demonstrates is that if you write to the parent dataspace prior to vforking, then all the threads will have the same value for for that variable. It also shows that you don't understand how sleep() is implemented. Finally, this should be its own question because it is a question, not an answer.
    – msw
    Oct 23, 2014 at 16:38
  • 2
    Which variable do you mean in particular? Could you please run this code and explain to me why all these pthreads perform actions while they should be suspended? Is it because sleep unsuspends them? I don't think that using sleep is crucial for this demo, because we see this behaviour in production, while we do not do sleep's there :/
    – qbolec
    Oct 23, 2014 at 18:49
  • 1
    Please present this as a new question and I'll be happy to. I'm not trying to be obnoxious, but them's the StackOverflow rules (and they make sense for a site that wants to be pure Q&A)
    – msw
    Oct 23, 2014 at 19:44
  • 1
    @msw thanks, here is the question: unix.stackexchange.com/questions/163947/pthreads-and-vfork
    – qbolec
    Oct 24, 2014 at 7:56
0

When child process is spawned is parent process suspended?

Yes.

If yes why?

Because the parent and child are sharing address space. In particular, address space of the stack. If the parent tried to continue on it would likely call another function and trash the child process's call stack. So it does not run until exec() or _exit()

They can run in parallel (like Threads)? After all both threads and process call the same clone() function.

Indeed. Only the calling thread is suspended. See above.

How does parent process know that the child has exited?

It calls wait4(). But rather, you ask how the parent knows to continue. It doesn't. The kernel makes it runnable again when either of the two defined events occur.

And what will happen if we return from a child process?

vfork() returns into a freed stack frame, and the next return; is a fast path to undefined behavior.

0

What will happen if we return from a child process?

"It does not work, however, to return while running in the child's context from the procedure that called vfork() since the eventual return from vfork() would then return to a no longer existent stack frame."

I don't take this quite literally. Bytes in the stack region do not literally disappear after each POP (or RETURN) instruction. However if you return, keep running, and execute a PUSH (or CALL) instruction, it will replace the previous value on the stack.

It is just one prominent example of the weird things that are going to happen in general, if you call vfork() and then do anything to modify any of the memory of your process.

[Technically, I assume the same behaviour as in Linux. Other behaviours of vfork() were technically permitted by POSIX. I don't know if anyone found a use for the technical flexibility of POSIX, other than to provide a vfork() which is identical to fork()].

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .