Nyx by Example

Fork & Exec

fork() creates a child process. In the child (pid == 0), call execvp immediately to replace the process image — the Boehm GC requires this pattern since the child inherits an inconsistent GC state. waitpid in the parent blocks until the child exits.

Code

// Fork and exec — running external commands
// Fork y exec — ejecutar comandos externos

fn main() -> int {
    let pid: int = fork()

    if pid == 0 {
        // Child process: exec immediately (GC requirement)
        let args: Array = ["echo", "hello from child"]
        execvp("echo", args)
        // execvp replaces the process — this line never runs
        return 1
    }

    if pid > 0 {
        // Parent: wait for child to finish
        print("parent: spawned child " + int_to_string(pid))
        let status: int = waitpid(pid, 0)
        print("parent: child exited with " + int_to_string(status))
    } else {
        print("fork failed")
    }

    return 0
}

Output

parent: spawned child 12345
hello from child
parent: child exited with 0

Explanation

fork() duplicates the current process. It returns 0 in the child and the child's PID in the parent. Because Nyx uses the Boehm garbage collector, the child process must not allocate GC-managed memory — it must call execvp() immediately to replace its process image with a new program.

execvp(command, args) replaces the current process with the given command. The first element of args is conventionally the program name. If execvp succeeds, it never returns.

waitpid(pid, 0) blocks the parent until the child exits and returns the exit status. This is the standard Unix pattern for spawning subprocesses.

← Previous Next →

Source: examples/by-example/64-fork-exec.nx