4

I always hear that you should never use system() and instead fork/exec because system() blocks the parent process.

If so, am I doing something wrong by calling waitpid(), which also blocks the parent process when I do a fork/exec? Is there a way around calling waitpid...I always thought it was necessary when doing a fork/exec.

pid_t pid = fork();

if (pid == -1)
{
    // failed to fork
} 
else if (pid > 0)
{
    int status;
    waitpid(pid, &status, 0);
}
else 
{
    execve(...);
}
lolololol ol
  • 848
  • 1
  • 8
  • 18
  • 1
    You need to to `wait()` for the child process at some point to remove its from a zombie state, yes. But you don't have to wait for the child to die. Asynchronous notification is available via `SIGCHLD`, too. – dhke Mar 16 '17 at 17:30
  • 1
    That statement is nonsese about the given reason. `system` has other potential problems. Did you read the man-pages of the functions? – too honest for this site Mar 16 '17 at 17:32
  • 1
    the difference is that `system` blocks some signals that you might want your process to catch and deal with such as SIGINT, where as using `fork`/`waitpid` you can catch them and if need be, kill the child process and exit cleanly. – Chris Turner Mar 16 '17 at 17:32
  • 1
    If you don't need to do anything until the child finishes, then that reason doesn't apply to you. You should still use `fork/exec` because it doesn't use the shell. – Barmar Mar 16 '17 at 17:32
  • @ChrisTurner that makes so much sense now. – lolololol ol Mar 16 '17 at 17:35
  • @Olaf Yes, silly me. I changed the title, accordingly. – lolololol ol Mar 16 '17 at 17:35

3 Answers3

9

The WNOHANG flag (set in the options argument) will make the call to waitpid() non-blocking.

You'll have to call it periodically to check if the child is finished yet.

Or you could setup SIGCHLD to take care of the children.

Addison
  • 7,322
  • 2
  • 39
  • 55
Attie
  • 6,690
  • 2
  • 24
  • 34
  • What is purpose of calling waitpid, then? In case any children have finished, they can report their status? – lolololol ol Mar 16 '17 at 23:51
  • 3
    Something has to tidy up after the children, and you'll generally want to know if a child succeeded or not. `waitpid()` will accomplish both of these things. If you don't call it, then children that have turned into zombies (defunct processes) are never tidied away and keep using up resources. – Attie Mar 16 '17 at 23:55
  • But if I start a bunch of child processes and none of them have exited, `waitpid()` with `WNOHANG` returns immediately. How would it know if a child succeeded or not? Also, how does it tidy up them up? My guess is it would think there's no child processes. – lolololol ol Mar 17 '17 at 05:32
  • Correct. You have to keep calling `waitpid()` until all your children have died. Or look at using the `SIGCHLD` signal. As per my answer. – Attie Mar 17 '17 at 07:45
3

If you want to do other stuff whilst the child process is off doing it's thing, you can set up a trap for SIGCHLD that handles the child finishing/exiting. Like in this very simple example.

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

pid_t pid;
int finished=0;

void zombie_hunter(int sig)
    {
    int status;
    waitpid(pid, &status, 0);
    printf("Got status %d from child\n",status);
    finished=1;
    }

int main(void)
    {
    signal(SIGCHLD,zombie_hunter);

    pid = fork();

    if (pid == -1)
        {
        exit(1);
        } 
    else if (pid == 0)
        {
        sleep(10);
        exit(0);
        }

    while(!finished)
        {
        printf("waiting...\n");
        sleep(1);
        }
    }
Chris Turner
  • 8,082
  • 1
  • 14
  • 18
  • 1
    _The behavior of `signal()` varies across UNIX versions, and has also varied historically across different versions of Linux. Avoid its use: use sigaction(2) instead._ – Attie Mar 16 '17 at 17:57
  • it needs at the top : #include #include #include #include #include otherwise the example doesnt compile. – Alex May 14 '22 at 00:01
1

I always hear that you should never use system() and instead fork/exec because system() blocks the parent process.

Never say never. If system() has the semantics you want, including, but not limited to, blocking the calling process, then by all means, use it! Do be sure that you understand all those semantics, though.

If your objective is to avoid blocking the parent process, then it is important to understand that the parent can perform an unbounded amount of work between forking a child and collecting it via one of the wait() family of functions. This is very much analogous to starting a new thread, proceeding on with other work, and then eventually joining the thread.

Moreover, if the parent doesn't need to know or care when the child terminates, then it is possible to avoid any need to wait for it at all, ever.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • 2
    Last sentence isn't true. Every child process needs to be reaped, either by the parent, or if the parent exits, by `init `(PID 1) – Ben Voigt Mar 16 '17 at 17:49
  • 2
    @BenVoigt, I primarily meant that the *parent* need not ever wait. Nevertheless, if the parent sets up a signal handler for `SIGCHLD` with flag `SA_NOCLDWAIT` then its children do not become zombies when they terminate, and they do not need to be collected. – John Bollinger Mar 16 '17 at 17:53
  • 1
    Ahh, I hadn't known about that flag, it looks like a good way to get reaping without `waitpid`. Apart from that flag, though, whether the parent needs to wait on the child depends not on whether it cares about the result, but whether it is long-running or not. A long-running parent needs to reap child processes it spawns, even if it doesn't care about them, via either the wait family of functions, or that `SA_NOCLDWAIT` flag. – Ben Voigt Mar 16 '17 at 18:12