fork(2)

๐ŸŽ€ NAME ๐ŸŽ€

fork โ€” create a child process ๐Ÿ’–

so like, quick intro bestie: this whole page is the manual for fork(2), and it lives in the System Calls Manual. it's giving "the origin story of every process you've ever met" โœจ


๐Ÿ“š LIBRARY ๐Ÿ“š

Standard C library (libc, -lc) ๐Ÿซถ


โœจ SYNOPSIS โœจ

#include <unistd.h>

pid_t fork(void);

๐Ÿ’… DESCRIPTION ๐Ÿ’…

so fork() creates a whole new process by duplicating the process that called it. the new baby one is called the child process ๐ŸŒฑ and the one that did the calling is the parent process. it's giving single parent, no cap.

the child and the parent run in separate memory spaces. at the exact moment of the fork(), both memory spaces hold the same content. and like โ€” memory writes, file mappings (mmap(2)), and unmappings (munmap(2)) done by one of them do not affect the other. they're independent, periodt.

the child is an exact duplicate of the parent except for these little differences, and bestie, some of these are lowkey important:

  • the child has its own unique process ID, and this PID does not match the ID of any existing process group (setpgid(2)) or session.
  • the child's parent process ID is the same as the parent's process ID. (iconic, actually)
  • the child does not inherit its parent's memory locks (mlock(2), mlockall(2)).
  • process resource utilizations (getrusage(2)) and CPU time counters (times(2)) get reset to zero in the child. fresh start, we love that.
  • the child's set of pending signals starts out empty (sigpending(2)).
  • the child does not inherit semaphore adjustments from its parent (semop(2)).
  • the child does not inherit process-associated record locks from its parent (fcntl(2)). (but hear me out โ€” it does inherit fcntl(2) open file description locks and flock(2) locks from its parent. iykyk ๐ŸŒˆ)
  • the child does not inherit timers from its parent (setitimer(2), alarm(2), timer_create(2)).
  • the child does not inherit outstanding asynchronous I/O operations from its parent (aio_read(3), aio_write(3)), and it also doesn't inherit any asynchronous I/O contexts from its parent (see io_setup(2)).

all those attributes up there โ˜๏ธ are specified in POSIX.1. but wait, there's more โ€” parent and child also differ on these Linux-specific process attributes:

  • the child does not inherit directory change notifications (dnotify) from its parent (see the description of F_NOTIFY in fcntl(2)).
  • the prctl(2) PR_SET_PDEATHSIG setting gets reset, so the child does not receive a signal when its parent terminates. ๐Ÿฅฒ
  • the default timer slack value is set to the parent's current timer slack value. see the description of PR_SET_TIMERSLACK in prctl(2).
  • memory mappings that have been marked with the madvise(2) MADV_DONTFORK flag are not inherited across a fork().
  • memory in address ranges marked with the madvise(2) MADV_WIPEONFORK flag gets zeroed in the child after a fork(). (and the MADV_WIPEONFORK setting itself stays in place for those address ranges in the child.)
  • the termination signal of the child is always SIGCHLD (see clone(2)).
  • the port access permission bits set by ioperm(2) are not inherited by the child โ€” the child has to turn on any bits it needs using ioperm(2) itself.

and there's even more, so stay with me:

  • the child process is created with a single thread โ€” the one that called fork(). the entire virtual address space of the parent is replicated in the child, including the states of mutexes, condition variables, and other pthreads objects. using pthread_atfork(3) may be helpful for dealing with any problems this can cause. (this is the part where things get spicy ๐Ÿ’€)
  • after a fork() in a multithreaded program, the child can safely call only async-signal-safe functions (see signal-safety(7)) until the moment it calls execve(2).
  • the child inherits copies of the parent's set of open file descriptors. each file descriptor in the child refers to the same open file description (see open(2)) as the corresponding file descriptor in the parent. meaning: the two fds share open file status flags, file offset, and signal-driven I/O attributes (see the description of F_SETOWN and F_SETSIG in fcntl(2)).
  • the child inherits copies of the parent's set of open message queue descriptors (see mq_overview(7)). each file descriptor in the child refers to the same open message queue description as the corresponding fd in the parent โ€” so the two share the same flags (mq_flags).
  • the child inherits copies of the parent's set of open directory streams (see opendir(3)). POSIX.1 says the corresponding directory streams in parent and child may share the directory stream positioning โ€” but on Linux/glibc they do not. slay ๐Ÿ’…

๐Ÿซถ RETURN VALUE ๐Ÿซถ

on success, the PID of the child process is returned in the parent, and 0 is returned in the child. yes bestie, that's the whole trick โ€” you check the return value and that's how you know which one you are โœจ

on failure, -1 is returned in the parent, no child process is created, and errno is set to tell you what went wrong.


๐Ÿ˜ญ ERRORS ๐Ÿ˜ญ

  • EAGAIN โ€” a system-imposed limit on the number of threads was hit. there are a few different limits that can trigger this one:
    • the RLIMIT_NPROC soft resource limit (set via setrlimit(2)), which limits the number of processes and threads for a real user ID, was reached;
    • the kernel's system-wide limit on the number of processes and threads, /proc/sys/kernel/threads-max, was reached (see proc(5));
    • the maximum number of PIDs, /proc/sys/kernel/pid_max, was reached (see proc(5)); or
    • the PID limit (pids.max) imposed by the cgroup "process number" (PIDs) controller was reached.
  • EAGAIN โ€” the caller is operating under the SCHED_DEADLINE scheduling policy and does not have the reset-on-fork flag set. see sched(7).
  • ENOMEM โ€” fork() failed to allocate the necessary kernel structures because memory is tight.
  • ENOMEM โ€” an attempt was made to create a child process in a PID namespace whose "init" process has terminated. see pid_namespaces(7).
  • ENOSYS โ€” fork() is not supported on this platform (for example, hardware without a Memory-Management Unit).
  • ERESTARTNOINTR (since Linux 2.6.17) โ€” system call was interrupted by a signal and will be restarted. (this one can only be seen during a trace ๐Ÿ”)

๐ŸŒˆ VERSIONS ๐ŸŒˆ

๐Ÿงš C library/kernel differences ๐Ÿงš

since glibc 2.3.3, instead of invoking the kernel's fork() system call, the glibc fork() wrapper โ€” provided as part of the NPTL threading implementation โ€” invokes clone(2) with flags that give the same effect as the traditional system call. (a call to fork() is equivalent to a call to clone(2) specifying flags as just SIGCHLD.) and the glibc wrapper also invokes any fork handlers that have been established using pthread_atfork(3).

๐Ÿฆ‹ Async-signal safety ๐Ÿฆ‹

_Fork(3) is an async-signal safe variant of fork(2).


๐Ÿ‘‘ STANDARDS ๐Ÿ‘‘

POSIX.1-2024.


๐Ÿ“œ HISTORY ๐Ÿ“œ

4.3BSD, SVr4, POSIX.1-1988.


๐Ÿ’œ NOTES ๐Ÿ’œ

under Linux, fork() is implemented using copy-on-write pages, so the only penalty it incurs is the time and memory required to duplicate the parent's page tables, and to create a unique task structure for the child. the way that it's literally so efficient... obsessed ๐Ÿ˜Œ


๐ŸŒธ EXAMPLES ๐ŸŒธ

see pipe(2) and wait(2) for more examples.

#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int
main(void)
{
    pid_t pid;

    if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) {
        perror("signal");
        exit(EXIT_FAILURE);
    }
    pid = fork();
    switch (pid) {
    case -1:
        perror("fork");
        exit(EXIT_FAILURE);
    case 0:
        puts("Child exiting.");
        fflush(stdout);
        _exit(EXIT_SUCCESS);
    default:
        printf("Child is PID %jd\n", (intmax_t) pid);
        puts("Parent exiting.");
        exit(EXIT_SUCCESS);
    }
}

๐Ÿ“ SEE ALSO ๐Ÿ“

clone(2), execve(2), exit(2), _exit(3), setrlimit(2), unshare(2), vfork(2), wait(2), daemon(3), _Fork(3), pthread_atfork(3), capabilities(7), credentials(7)


okay bestie that's the whole page, and ngl you ate that ๐Ÿซถ this was Linux man-pages 6.20, dated 2026-06-05, and the page itself is, you guessed it, fork(2). go forth and fork responsibly, mwah ๐Ÿ’–โœจ๐ŸŽ€

๐Ÿ“ฆ where this page comes from
  • arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
  • nixpkgs unstable: man-pages 6.20
  • ๐Ÿ“œ the page's license: Linux-man-pages-copyleft
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms