Create Processes With Fork in C++

  1. Use fork() to Create Two Processes Within the Program in C++
  2. Use fork() and execve to Create Multiple Processes in C++
  3. Use fork() and execve to Create Multiple Processes With Automatic Children Cleanup Function in C++

This article will explain several methods of how to create processes with a fork() system call in C++.

Use fork() to Create Two Processes Within the Program in C++

The fork function is the POSIX compliant system call available in most Unix-based operating systems. The function creates a new process, which is a duplicate of the original calling program. The latter process is called parent and a newly created one - child. These two processes can be seen as the two threads executing in separate memory spaces. Note that the current Linux implementation has no concept of thread internally, so threads are similar structures as processes except that they share memory regions. The fork function can implement concurrent execution within the same program or run a new executable from the filesystem (demonstrated in the later examples).

In the following example, we utilize fork to demonstrate multiprocessing within the one program. fork takes no arguments and returns in both processes. The return value is the PID of the child in the parent process, and 0 is returned in the child process. In case the call fails, -1 is returned in the parent process. Hence, we can construct if statements based on evaluation of the return value, and each if block gets executed by the corresponding process, resulting in concurrent execution.

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

using std::cout; using std::endl;

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

    if (c_pid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    } else if (c_pid > 0) {
        cout << "printed from parent process " << getpid() << endl;
        wait(nullptr);
    } else {
        cout << "printed from child process " << getpid() << endl;
        exit(EXIT_SUCCESS);
    }

    return EXIT_SUCCESS;
}

Output:

printed from parent process 27295
printed from child process 27297

Use fork() and execve to Create Multiple Processes in C++

More practical use of the fork function call is to create multiple processes and execute different programs within these processes. Note that, in this example, we need two source code files: one for the parent process and the other for child processes. Child process code is the simple infinite loop that adds to the single integer and can be stopped by sending the SIGTERM signal.

The parent program declares a filename that needs to be executed by the forked child processes and then calls the spawnChild function 6 times. The spawnChild function wraps the fork/execve calls and returns the newly created process ID. Notice that execve requires a program name and argument list as arguments to launch a new program code within the child processes. Once the 6 child processes are created, the parent continues in the while loop where it calls the wait function. wait stops the parent process and waits until any of the child processes terminates.

Note that you need to terminate each child processes for the parent to exit normally. If you interrupt the parent process, child processes will continue running, and their parents become a system process.

#include <iostream>
#include <vector>
#include <sys/wait.h>
#include <unistd.h>
#include <atomic>
#include <filesystem>

using std::cout; using std::endl;
using std::vector; using std::string;
using std::filesystem::exists;

constexpr int FORK_NUM = 6;

pid_t spawnChild(const char* program, char** arg_list)
{
    pid_t ch_pid = fork();
    if (ch_pid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    } else if (ch_pid > 0) {
        cout << "spawn child with pid - " << ch_pid << endl;
        return ch_pid;
    } else {
        execve(program, arg_list, nullptr);
        perror("execve");
        exit(EXIT_FAILURE);
    }
}

int main() {
    string program_name ("child");
    char *arg_list[] = {program_name.data(), nullptr};
    vector<int> children;
    children.reserve(FORK_NUM);

    if (!exists(program_name)){
        cout << "Program file 'child' does not exist in current directory!\n";
        exit(EXIT_FAILURE);
    }

    for (int i = 0; i < FORK_NUM; ++i)
        children[i] = spawnChild(program_name.c_str(), arg_list);
    cout << endl;

    pid_t child_pid;
    while ((child_pid = wait(nullptr)) > 0)
        cout << "child " << child_pid << " terminated" << endl;

    return EXIT_SUCCESS;
}

Child process source code (different file):

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

volatile sig_atomic_t shutdown_flag = 1;

void GracefulExit(int signal_number)
{
    shutdown_flag = 0;
}

int main()
{
    // Register SIGTERM handler
    signal(SIGTERM, GracefulExit);

    unsigned int tmp = 0;
    while (shutdown_flag) {
        tmp += 1;
        usleep(100);
    }

    exit(EXIT_SUCCESS);
}

Use fork() and execve to Create Multiple Processes With Automatic Children Cleanup Function in C++

The previous example code has clumsy behavior if the parent process got terminated before all the children exited. In this case, we add the signal handler function to the parent process that will automatically terminate all children processes once the SIGQUIT signal is received. Use kill -SIGQUIT pid_num_of_parent command to send the signal.

Note that some of the global variables that need to be accessed in the signal handler are declared as std::atomic types, which is the strict requirement for program correctness.

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

using std::cout; using std::endl;
using std::string; using std::filesystem::exists;

constexpr std::atomic<int> FORK_NUM = 6;
constexpr std::atomic<int> handler_exit_code = 103;

std::atomic<int> child_pid;
std::atomic<int> *children;

void sigquitHandler(int signal_number)
{
    for (int i = 0; i < FORK_NUM; ++i) {
        kill(children[i], SIGTERM);
    }
    while ((child_pid = wait(nullptr)) > 0);
    _exit(handler_exit_code);
}

pid_t spawnChild(const char* program, char** arg_list)
{
    pid_t ch_pid = fork();
    if (ch_pid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    } else if (ch_pid > 0) {
        cout << "spawn child with pid - " << ch_pid << endl;
        return ch_pid;
    } else {
        execve(program, arg_list, nullptr);
        perror("execve");
        exit(EXIT_FAILURE);
    }
}

int main() {
    string program_name ("child");
    char *arg_list[] = {program_name.data(), nullptr};

    if (!exists(program_name)){
        cout << "Program file 'child' does not exist in current directory!\n";
        exit(EXIT_FAILURE);
    }

    children = reinterpret_cast<std::atomic<int> *>(new int[FORK_NUM]);
    signal(SIGQUIT, sigquitHandler);

    for (int i = 0; i < FORK_NUM; ++i) {
        children[i] = spawnChild(program_name.c_str(), arg_list);
    }
    cout << endl;

    while ((child_pid = wait(nullptr)) > 0)
        cout << "child " << child_pid << " terminated" << endl;

    return EXIT_SUCCESS;
}