Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to fork/exec and guarantee one starts before the other?

Pretty much as the title says. I have a snippet of code that looks like this:

pid_t = p;

p = fork();

if (p == 0) {
    childfn();
} else if (p > 0) {
    parentfn();
} else {
    // error
}

I want to ensure that either the parent or the child executes (but not returns from) their respective functions before the other.

Something like a call to sleep() would probably work, but is not guaranteed by any standard, and would just be exploiting an implementation detail of the OS's scheduler...is this possible? Would vfork work?

edit: Both of the functions find their way down to a system() call, one of which will not return until the other is started. So to re-iterate: I need to ensure that either the parent or the child only calls their respective functions (but not returns, cause they won't, which is what all of the mutex based solutions below offer) before the other. Any ideas? Sorry for the lack of clarity.

edit2: Having one process call sched_yield and sleep, I seem to be getting pretty reliable results. vfork does provide the semantics I am looking for, but comes with too many restrictions on what I can do in the child process (I can pretty much only call exec). So, I have found some work-arounds that are good enough, but no real solution. vfork is probably the closest thing to what I was looking for, but all the solutions presented below would work more or less.

like image 887
user318904 Avatar asked Aug 31 '11 19:08

user318904


People also ask

How is fork () and exec () calls different from each other?

fork vs execfork starts a new process which is a copy of the one that calls it, while exec replaces the current process image with another (different) one. Both parent and child processes are executed simultaneously in case of fork() while Control never returns to the original program unless there is an exec() error.

Why do we need separate fork () and exec () system calls instead of one combined system call that does both?

The main reason is likely that the separation of the fork() and exec() steps allows arbitrary setup of the child environment to be done using other system calls.

Does Execve create a new process?

Like all of the exec functions, execve replaces the calling process image with a new process image. This has the effect of running a new program with the process ID of the calling process. Note that a new process is not started; the new process image simply overlays the original process image.

How are fork () and exec () system calls different from normal system function calls in terms of their call return behavior?

A call to fork() returns once or twice - the latter on success where it returns once in the parent and once in the child, the former on failure where it simply returns once in the parent. A call to exec() will return on failure but, if successful, the current process is simply overwritten with a new program.


1 Answers

This problem would normally be solved by a mutex or a semaphore. For example:

// Get a page of shared memory
int pagesize = getpagesize();
void *mem = mmap(NULL, pagesize, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
if(!mem)
{
  perror("mmap");
  return 1;
}

// Put the semaphore at the start of the shared page.  The rest of the page
// is unused.
sem_t *sem = mem;
sem_init(sem, 1, 1);

pid_t p = fork();

if (p == 0) {
    sem_wait(sem);
    childfn();
    sem_post(sem);
} else if (p > 0) {
    sem_wait(sem);
    parentfn();
    sem_post(sem);

    int status;
    wait(&status);
    sem_destroy(sem);
} else {
    // error
}

// Clean up
munmap(mem, pagesize);

You could also use a mutex in a shared memory region, but you need to make sure to create with non-default attributes with the process-shared attribute said to shared (via pthread_mutexattr_setpshared(&mutex, PTHREAD_PROCESS_SHARED)) in order for it to work.

This ensures that only one of childfn or parentfn will execute at any given time, but they could run in either order. If you need to have a particular one run first, start the semaphore off with a count of 1 instead of 0, and have the function that needs to run first not wait for the semaphore (but still post to it when finished). You might also be able to use a condition variable, which has different semantics.

like image 149
Adam Rosenfield Avatar answered Sep 18 '22 00:09

Adam Rosenfield