fork(), waitpid() and timeout in Linux C++

Introducing the fork() and waitpid() examples in Linux C ++ programming. Calling waitpid() blocks until the child process terminates. We will also introduce an example that applies a timeout and does not wait when the child does not respond.

Note that i don`t know the Linux system deeply, there may be something wrong.

fork()

fork() is an API that creates a child process by duplicating the parent`s process.

Here is a simple example using fork().

#include <iostream>
#include <unistd.h>

int main() {
    pid_t pid = fork();

    if (pid < 0) {
        fprintf(stderr, "Fork failed!\n");
        exit(-1);
    }

    if (pid == 0) {
        // child process
        printf("(%ld) I am the child, pid: %d, getpid(): %d\n",
                time(0), pid, getpid());
        sleep(2);
        printf("(%ld) Done from child\n", time(0));
        exit(0);
    } else {
        // parent process
        printf("(%ld) I am the parent, pid=%d\n", time(0), pid);
        sleep(4);
        printf("(%ld) Done from parent\n", time(0));
        exit(0);
    }
}

Execution result. The beginning of the log is the system time, in seconds.

(1578224091) I am the parent, pid=24139
(1578224091) I am the child, pid: 0, getpid(): 24139
(1578224093) Done from child
(1578224095) Done from parent

If fork() succeeds, the parent process returns the child`s pid, and the child process returns zero. If fork() fails, such as, lack of memory, -1 is returned.

waitpid()

waitpid() is used when parent waits for child to exit.

The following example waits for the parent to terminate the child process with waitpid().

#include <iostream>
#include <unistd.h>
#include <sys/wait.h>

int main() {
    pid_t pid = fork();

    if (pid < 0) {
        fprintf(stderr, "Fork failed!\n");
        exit(-1);
    }

    if (pid == 0) {
        // child process
        printf("(%ld) I am the child, pid: %d, getpid(): %d\n",
                time(0), pid, getpid());
        sleep(2);
        printf("(%ld) Done from child\n", time(0));
        exit(0);
    } else {
        // parent process
        printf("(%ld) I am the parent, pid=%d\n", time(0), pid);

        int state;
        pid_t got_pid = waitpid(pid, &state, 0);  // 1

        printf("(%ld) got_pid=%d\n", time(0), got_pid);   // 2
        printf("(%ld) WIFEXITED: %d\n", time(0), WIFEXITED(state));  // 3
        printf("(%ld) WEXITSTATUS: %d\n", time(0), WEXITSTATUS(state)); // 4
        printf("(%ld) Done from parent\n", time(0));
        exit(0);
    }
}
  1. As argument to waitpid(), pass the pid of the waiting child, the status to be returned, and the option. If the state of the child process changes (even if it is not terminated), waitpid() is returned.
  2. If it returns successfully, the child`s pid is returned. Otherwise -1 is returned.
  3. WIFEXITED is a macro that checks to see if the child has terminated in state. Returns true if terminated.
  4. WEXITSTATUS returns exit status.

result

(1578224731) I am the parent, pid=25194
(1578224731) I am the child, pid: 0, getpid(): 25194
(1578224733) Done from child
(1578224733) got_pid=25194
(1578224733) WIFEXITED: 1
(1578224733) WEXITSTATUS: 0
(1578224733) Done from parent

For more information about waitpid status and options, see man page or waitpid.

Timeout waitpid() (1)

There is a way to call waitpid() after receiving the signal and checking if the child has terminated.

#include <iostream>
#include <unistd.h>
#include <sys/wait.h>

static pid_t fork_child() {
    int p = fork ();
    if (p == -1) {
        exit (1);
    }

    if (p == 0) {
        printf ("(%ld) child: sleeping...\n", time(0));
        sleep (10);
        printf ("(%ld) child: exiting\n", time(0));
        exit (0);
    }

    return p;
}

int main() {
    sigset_t mask;
    sigset_t orig_mask;
    struct timespec timeout;
    timeout.tv_sec = 5;
    timeout.tv_nsec = 0;

    sigemptyset (&mask);
    sigaddset (&mask, SIGCHLD);
    if (sigprocmask(SIG_BLOCK, &mask, &orig_mask) < 0) {
        perror ("sigprocmask");
        return 1;
    }

    pid_t pid = fork_child();
    do {
        if (sigtimedwait(&mask, NULL, &timeout) < 0) {
            if (errno == EINTR) {
                // Interrupted by a signal other than SIGCHLD.
                continue;
            } else if (errno == EAGAIN) {
                printf ("(%ld) Timeout, killing child\n", time(0));
                kill (pid, SIGKILL);
            } else {
                printf ("(%ld) sigtimedwait\n", time(0));
                return 1;
            }
        }
        break;
    } while (1);

    int state;
    if (waitpid(pid, &state, 0) < 0) {
        printf("(%ld) WIFEXITED: %d\n", time(0), WIFEXITED(state));
        printf("(%ld) WEXITSTATUS: %d\n", time(0), WEXITSTATUS(state));
        printf("(%ld) Done from parent\n", time(0));
        return 1;
    }
}

result

(1578225826) child: sleeping...
(1578225831) Timeout, killing child

Timeout waitpid() (2)

There is another way to timeout a waitpid() using an alarm. if timed out waiting for finishing a waitpid(), the handler kills the child process.

#include <iostream>
#include <unistd.h>
#include <sys/wait.h>

int child_pid = 0;
void timer_handler(int pid) {
  printf("(%ld) time is over, child will be killed\n", time(0));
  kill(child_pid, SIGKILL);
}

int main() {
    signal(SIGALRM, timer_handler);

    pid_t pid = fork();
    child_pid = pid;

    if (pid == 0) { // child process
        printf("(%ld) I am the child, return from fork=%d\n",
                time(0), pid);
        sleep(20);
        printf("(%ld) Done from child\n", time(0));
    } else { // parent process
        printf("(%ld) I am the parent, child pid=%d\n",
                time(0), pid);

        alarm(5);

        int state;
        waitpid(pid, &state, 0);
        printf("(%ld) WIFEXITED: %d\n", time(0), WIFEXITED(state));
        printf("(%ld) WEXITSTATUS: %d\n", time(0), WEXITSTATUS(state));
        printf("(%ld) Done from parent\n", time(0));
        exit(0);
    }
}

result

(1578226328) I am the parent, child pid=27412
(1578226328) I am the child, return from fork=0
(1578226333) time is over, child will be killed
(1578226333) WIFEXITED: 0
(1578226333) WEXITSTATUS: 0
(1578226333) Done from parent

Reference

codechachaCopyright ©2019 codechacha