Forking in Zig

Zig makes it straightforward to work with fundamental operating system concepts such as process creation and management. This program demonstrates how a single Zig executable can split into two separate running processes using the classic forking technique. Upon execution, the main() function first retrieves the current process ID by calling getpid(). It then uses the fork() function to create a child process, resulting in two concurrent instances of the program.

const std = @import("std");
const c = @cImport({
    @cInclude("unistd.h");
    @cInclude("sys/wait.h");
});

pub fn main(init: std.process.Init) !void {
    _ = init;
    const myPID = c.getpid();
    const pid = c.fork();
    if (pid < 0) {
        std.debug.print("Fork failed.\n", .{});
        return error.ForkFailed;
    } else if (pid == 0) {
        std.debug.print("Hello from the child process!\n", .{});
    } else {
        std.debug.print("Hello from the parent process!\n", .{});
        std.debug.print(
            "Parent PID: {} - Child PID: {}\n",
            .{ myPID, pid },
        );
        const wait_result = c.waitpid(pid, null, 0);
        if (wait_result < 0) {
            std.debug.print("waitpid failed.\n", .{});
        }
    }
}

The program functionality relies on the return value of the fork() call to determine the behavior of each process. When the returned PID is less than zero, it indicates a failure and the program prints an error message while returning the ForkFailed error. In the child process, where the PID equals zero, execution flows into a branch that prints the message “Hello from the child process!” using std.debug.print(). This child then terminates naturally after its output.

In the parent process branch, the code prints “Hello from the parent process!” followed by a formatted message displaying both the parent PID (captured earlier via getpid()) and the child PID returned by fork(). The parent then calls waitpid() to block until the child process completes, ensuring it waits for the child termination before continuing. Error checking is performed on the result of waitpid() to handle any potential issues during synchronization.

Save the code as forkMe.zig and run it (your output will vary):

$ zig run forkMe.zig
Hello from the child process!
Hello from the parent process!
Parent PID: 90047 - Child PID: 90050

Happy coding in Zig!