Fact-checked by Grok 2 weeks ago

Child process

In , a child process is a new process created by an existing process, referred to as the , within multitasking operating systems to enable concurrent execution of tasks. This creation typically involves duplicating the parent's execution context, including memory, open files, and environment variables, though the child operates independently with its own (PID). The parent-child relationship facilitates resource management, such as inheritance of file descriptors and signal handling, and is fundamental to process hierarchies in . In Unix-like systems, child processes are commonly created using the fork() system call defined in the standard, which returns zero to the child and the child's to the parent, allowing both to execute different code paths from the point of the call. For instance, the child might use an exec() family function to load a new program while retaining the parent's environment. In contrast, Microsoft Windows employs the CreateProcess() to spawn a child process, specifying the executable path, command-line arguments, and security attributes, without duplicating the parent's directly. Child processes inherit certain attributes from their parents, such as and user credentials, but they have separate address spaces to ensure and prevent interference. The parent can monitor the child's status via the wait() system call in POSIX environments or by handling the process handle in Windows, enabling synchronization and cleanup upon termination. This model supports essential operations like executing shell commands, , and daemonization, where long-running services detach from the parent. The concept of child processes underpins process scheduling and in operating systems, promoting and scalability in applications ranging from servers to systems. By allowing hierarchical process trees, it enables efficient multitasking while maintaining security boundaries between parent and child.

Fundamentals

Definition

A child process is a new process created by an existing process, known as the , in multitasking operating systems such as Unix, , and Windows. This creation results in the child inheriting specific attributes from the parent while operating as an independent entity with its own . Unlike threads, which share the same and within a single , child processes maintain separate , providing isolation between their and execution contexts. The child inherits key attributes from the parent, including open file descriptors, the current , environment variables, and and group IDs, but does not fully duplicate the parent's contents. In systems, inheritance uses mechanisms, where the child initially shares read-only pages with the parent, and a private copy is created only upon modification by either . Child processes play a crucial role in enabling parallelism, modularity, and resource isolation in operating systems, allowing multiple programs or tasks to execute concurrently without interfering with each other. This separation supports robust multitasking environments by limiting the scope of faults and facilitating hierarchical process management.

Key Properties

Upon creation, a child process in operating systems receives a unique (PID), which does not match its parent's PID or any active ID. The child inherits the parent's ID and , ensuring independent identification within the system. The child can access its parent's PID using the getppid() , which returns the PID of the calling process's parent without error. This assignment establishes the foundational link in the parent-child relationship, allowing processes to query and interact hierarchically. In terms of , the child inherits a copy of the parent's through a (COW) mechanism, where physical pages are shared initially and duplicated only upon modification by either to maintain . This approach optimizes efficiency by avoiding immediate full duplication, with changes in one not affecting the other's . The child does not inherit memory locks set by the parent via mlock() or mlockall(). For signal handling, the child inherits a copy of the parent's signal dispositions (actions for each signal, such as default, ignore, or catch) and signal mask (blocked signals), enabling it to respond similarly unless modified post-creation. However, the child's set of pending signals starts empty, preventing automatic propagation of the parent's pending signals, and signals sent to the parent do not reach the child by default. Child processes contribute to a hierarchical , where each child is a direct descendant of its parent, forming a tree rooted at the init (PID 1) in traditional Unix systems or in modern distributions, which serves as the ultimate ancestor for all . This tree organizes system resources and enables traversal via tools like pstree, reflecting parent-child dependencies. Resource limits, such as those managed by ulimit (e.g., maximum , , or number of open files), are inherited from the parent by the child , applying the same constraints unless explicitly altered using functions like setrlimit(). These per- limits, preserved across execution, help enforce system policies on resource usage in the child.

Historical Development

Origins in Early Operating Systems

The concept of child processes emerged in the 1960s as operating systems transitioned from rigid batch processing to more flexible time-sharing and multiprogramming environments, enabling dynamic creation of subprocesses for improved modularity and resource utilization. Early systems like the Compatible Time-Sharing System (CTSS), developed at MIT starting in 1961, introduced precursor ideas by allowing multiple users to execute commands as independent program loads without requiring full system restarts, using mechanisms such as the LOAD and START commands to initiate user programs in a shared core environment. This approach handled up to 30-38 concurrent users via a priority-based scheduling queue, treating each command execution as a distinct process burst to facilitate interactive command processing and multi-user access. Building on CTSS, —initiated in 1965 as a collaborative project by , , and —advanced these ideas by explicitly supporting the spawning of subprocesses from a to run asynchronously across multiple processors, promoting in time-sharing systems. In Multics, a process could create subordinate processes to handle tasks independently, with each process maintaining its own space through segmentation and paging, allowing for efficient subdivision of jobs without disrupting the parent. This design emphasized resource pooling and dynamic allocation, laying groundwork for hierarchical execution in multi-user environments. Concurrently, the Burroughs Master Control Program (MCP), introduced in for the B5000 series, implemented hierarchical process creation through "jobs" that contained one or more "tasks," where a job could tasks to execute sequentially or in for applications. MCP enforced - relationships via indices in Program Reference Tables (PRTs), enabling resource sharing such as re-entrant code and disk access through mechanisms like SHAREDISK, while ensuring isolation with unique PRT rows and file locking to prevent interference. Tasks inherited parameters from parents but operated in protected stacks, supporting without full system reconfiguration. This shift from batch job sequencing—where programs ran serially on 1960s mainframes like the IBM 7090—to dynamic subprocesses enabled pipelined execution, overlapping CPU computation with I/O operations to boost throughput in emerging multiprogrammed systems. These innovations in CTSS, , and MCP provided the foundational concepts for later standardized mechanisms, such as the Unix , by demonstrating the value of parent-child hierarchies in managing concurrency and isolation.

Introduction in Unix

In Unix Version 1, released in 1971 by Ken Thompson and Dennis Ritchie at Bell Labs, the child process concept was formalized through the introduction of the fork() system call, a primitive that duplicates an existing process to create an identical child process sharing the parent's memory image and open files but running independently. This mechanism enabled efficient process creation on the limited hardware of the PDP-11 computer, supporting multitasking and interactive use without the overhead of loading entire programs from scratch. The design of fork() emphasized simplicity and lightweight duplication, aligning with the emerging of building small, composable tools that could be chained together for complex tasks. For instance, it facilitated shell scripting and command pipelines, where the shell uses fork() to child processes that communicate via (introduced shortly after in ), allowing data to flow sequentially through modular utilities like filters and processors. This approach, implemented in just 27 lines of assembly code, avoided more complex alternatives like a combined fork-exec operation, drawing inspiration from earlier time-sharing systems to promote modularity and ease of implementation. Early implementations had limitations, as fork() produced an exact duplicate of the without integrated program loading; the child typically invoked exec() immediately to replace its image with a new program, a pattern driven by the shell's needs but lacking support for background processes or scripted command sequences at the outset. These constraints reflected the system's initial focus on text processing for patent documents on constrained hardware. The fork() model profoundly influenced operating system design, becoming a cornerstone of the standards developed in the 1980s, which standardized process creation across Unix variants. It was adopted in (BSD) for enhanced networking and research, System V for commercial deployments, and later in kernels, with the 1973 rewrite of Unix by Ritchie solidifying portability and multi-programming capabilities that propagated the child process paradigm widely.

Creation Methods

Fork Mechanism

The fork() system call in Unix-like operating systems creates a new child process by duplicating the calling parent process, resulting in two nearly identical processes that continue execution from the point immediately after the fork() invocation. The child process receives an exact copy of the parent's memory image, file descriptors, and other resources, except for specific differences such as the process ID (PID), parent PID, and certain scheduling attributes. Upon successful execution, fork() returns 0 to the child process, allowing it to distinguish itself from the parent, while it returns the child's PID (a positive integer) to the parent; on failure, it returns -1 to the parent with the errno variable set to indicate the error, and no child process is created. In modern implementations, such as in the , fork() employs a (COW) mechanism to optimize memory duplication, where the child's page tables are initially shared with the parent, but marked as read-only; physical memory pages are copied only when either attempts to write to them, thereby reducing the initial overhead of process creation to primarily duplicating the page tables and task structures rather than the entire . This COW approach, standard in systems since the 1990s, defers full copying until necessary, making fork() efficient for scenarios where the child quickly replaces its image or shares read-only data with the parent. Following the fork(), both and processes resume execution concurrently at the instruction after the , with the child often proceeding to an exec() family call to overlay a new program image while inheriting the parent's and open files. This immediate post-fork execution enables flexible process hierarchies without blocking the parent. The fork() call can fail under various conditions, such as when the system-imposed limit on the total number of processes per user ({CHILD_MAX}) or overall system resources is exceeded, resulting in an EAGAIN ; similarly, temporary resource shortages or insufficient memory for new structures may trigger EAGAIN or ENOMEM. In , additional limits like RLIMIT_NPROC, /proc/sys/kernel/threads-max, or cgroup PID constraints can also cause failure. A common use case for fork() is in Unix shells to execute background jobs, where appending an (&) to a command line prompts the to invoke fork() to create a child process that runs the specified program asynchronously, allowing the to return control to the user immediately without waiting for completion. For instance, entering [ls](/page/Ls) & in a forks a child to list contents in the background, printing the child's and enabling the to accept further input.

Spawn Mechanism

The spawn mechanism provides a direct method for creating and executing a new from a specified , without first duplicating the parent process's , distinguishing it from the two-step fork-exec approach used in systems. This integrated primitive is prevalent in non-Unix environments, such as Windows, where it enables efficient one-off program launches by combining initialization with loading the target in a single . In Windows, the primary spawn primitive is the CreateProcess API, introduced with in 1993, which creates a new and its primary to run an module in the security context of the calling . Key parameters include lpApplicationName for the executable path, lpCommandLine for arguments (up to 32,767 characters), lpEnvironment for a custom block (or from the parent if NULL), bInheritHandles to control handle , and lpStartupInfo to specify details like the and standard I/O handles. Additional options allow customization of the security context via user tokens (e.g., through CreateProcessAsUser) and flags to manage resource sharing between parent and child. The function returns immediately after initiating the , with output via lpProcessInformation capturing the process and IDs for further management. Complementing the native , Windows inherits spawn variants from its roots through the C runtime library's _spawn family of functions, which create and execute a new without duplicating the parent's state. Variants include _spawnl for passing arguments individually, _spawne for overriding the with a custom array of NAME=value strings, and their p-suffixed counterparts (_spawnlp, _spawnlpe) that search the for the executable. These functions operate in modes such as _P_WAIT for synchronous execution (suspending the parent until completion) or _P_NOWAIT for asynchronous, allowing the parent to continue while monitoring the . Unlike exec functions, _spawn modes permit the to persist and regain control, making it suitable for scripting scenarios like Perl's system() calls on Windows, where temporary subprocesses handle discrete tasks. The spawn mechanism's primary advantage lies in its reduced overhead for one-off executions, as it avoids the page table duplication of the fork-based approach, which can be noticeable for processes with large virtual address spaces. This efficiency is particularly beneficial in environments requiring frequent subprocess launches, such as command shells or interpreters, without the resource waste of an intermediate clone. Cross-platform support for spawn emerged with the POSIX.1-2008 standard, introducing posix_spawn as a fork alternative for systems like embedded or real-time environments where full address space duplication is impractical or unsupported. This function creates a child process from a specified pathname (or PATH-searched file via posix_spawnp), with parameters for file actions (to modify descriptor inheritance), attributes (e.g., signal masks, scheduling policy), argument arrays (argv), and environment arrays (envp). By bypassing fork's overhead, posix_spawn enables efficient process creation without memory management units or swapping, prioritizing minimal resource use in constrained systems.

Lifecycle Management

Initialization Phase

The initialization phase of a child process varies depending on the creation mechanism. In systems using the fork-exec model, such as many environments, the child is created as a copy of the via fork(), and the phase begins immediately, involving setup steps in the child that prepare it for independent execution while inheriting key attributes from the . The child receives a copy of the 's environment variables, open file descriptors, and resource limits, allowing it to operate in a familiar context before any transformations. In this model, the child typically invokes one of the exec family of functions, such as execve(), to replace its process image with a new program. This call overlays the child's segments—including text, , , and —with the contents of the , while preserving the process ID and other attributes like open file descriptors unless marked for closure. File descriptors remain open across the exec, enabling inheritance of resources such as standard , and error streams, which can be redirected in the child prior to the call—for instance, to connect to for . The child may also customize its environment before executing the new program by modifying the inherited environment variables passed via the envp argument to execve(). These modifications allow tailoring of the execution context, such as setting specific paths or variables for the target application, without altering the parent's state. Additionally, the allocates initial resources like a new and for the child during creation, and the child can further adjust resource limits—such as maximum file sizes or process counts—using setrlimit() before the exec. These limits are inherited from the parent and persist through the exec, providing fine-grained control over the child's resource consumption. In contrast, spawn mechanisms like posix_spawn() in POSIX systems or CreateProcess() in Windows handle initialization differently. For posix_spawn(), the parent specifies pre-execution actions (e.g., file descriptor operations, signal masks, environment modifications, and resource limits) via attributes and file actions before the call; the child process is created and the new executable is overlaid atomically, beginning execution directly at the program's entry point without child-side invocation of exec or intermediate setup code. Similarly, in Windows, the parent configures inheritance of handles, environment, and other attributes at creation time via CreateProcess(), and the child starts suspended before being resumed to run the specified executable directly. Synchronization between parent and child is essential during this phase to manage potential race conditions, particularly in the fork-exec model if the child's exec fails and it exits prematurely, risking an early zombie state. The parent can use signals like SIGCHLD to detect child state changes and invoke waitpid() promptly, ensuring timely reaping and avoiding accumulation of defunct processes in the kernel's process table. In spawn models, failure typically prevents child creation altogether, though internal exec failures may lead to immediate child exit. For efficiency in fork-exec scenarios where the child only performs an exec without needing the full duplicated , the vfork() minimizes memory duplication by sharing the parent's until the exec or occurs. However, vfork() is obsoleted in modern standards (removed from the base specification in POSIX.1-2017 but retained in extensions) due to its restrictive semantics and potential for errors if the modifies shared data. Instead, posix_spawn() is preferred as an optimized alternative, internally using vfork() or similar lightweight cloning in implementations like to reduce overhead while supporting pre-exec actions like management and signal mask adjustments.

Termination Phase

The termination of a child process in operating systems is initiated through system calls such as exit() or _exit(), which allow the process to end execution and return a code to its . The exit() function performs cleanup actions, including calling functions registered with atexit(), flushing open streams, closing file descriptors, and removing temporary files created by tmpfile(), before terminating the process and notifying the with the least significant byte of the via wait() or waitpid(). In contrast, _exit() (or the equivalent _Exit()) terminates the process immediately without invoking cleanup handlers or flushing streams, directly releasing resources such as memory, file descriptors, and the process ID (), while still providing the to the . Upon termination, the marks the child as a , retaining minimal information like its and in the process table until the retrieves it, preventing immediate full resource reclamation to allow reporting. Zombie processes arise when a child terminates but the parent does not yet call a wait system call to collect its exit status, causing the defunct entry to persist and potentially consume kernel process table slots if numerous such processes accumulate, leading to resource exhaustion. The parent is responsible for reaping these zombies using wait() or waitpid(); the former blocks until any child terminates and returns its PID along with status details in a provided integer, while waitpid() offers more control, specifying a particular child PID, process group, or any child, and supporting options like WNOHANG for non-blocking checks that return zero if no child has changed state. Status retrieval via these calls allows analysis using macros such as WIFEXITED() to check for normal exit and WEXITSTATUS() to extract the code, ensuring the zombie entry is cleared from the process table upon successful reaping. When a child process terminates, the kernel sends a SIGCHLD signal to the parent, indicating the child's state change (termination, stop, or continuation in POSIX-compliant systems), with the default action being to ignore it, though handlers can automate responses like reaping in shells for job control. This signal carries details via siginfo_t, including the child's , status, and user ID, enabling efficient parent notification without constant polling. If the parent process terminates before the child, the child becomes orphaned and is automatically reparented to the process (PID 1) or the namespace's equivalent init, which acts as a to collect the child's status upon its eventual termination, preventing persistent zombies. This reparenting ensures system stability by assigning a reliable adopter that routinely handles orphaned children through wait calls.

References

  1. [1]
    fork - The Open Group Publications Catalog
    The fork() function shall create a new process. The new process (child process) shall be an exact copy of the calling process (parent process) except as ...
  2. [2]
    fork(2) - Linux manual page - man7.org
    fork() creates a new process by duplicating the calling process. The new process is referred to as the child process. The calling process is referred to as the ...
  3. [3]
    Create processes - Win32 apps - Microsoft Learn
    Jul 14, 2025 · The CreateProcess function creates a new process that runs independently of the creating process. For simplicity, this relationship is called a parent-child ...
  4. [4]
    CreateProcessA function (processthreadsapi.h) - Win32 apps
    Feb 8, 2023 · This can be useful for synchronization between parent and child processes, because CreateProcess returns without waiting for the new process to ...Syntax · Parameters
  5. [5]
    Child Process - Artifact Details | MITRE D3FEND™
    A child process in computing is a process created by another process (the parent process). This technique pertains to multitasking operating systems, and is ...
  6. [6]
    Child Processes - Win32 apps - Microsoft Learn
    Jul 14, 2025 · Each process provides the resources needed to execute a program. A child process is a process that is created by another process, called the parent process.
  7. [7]
    Operating Systems: Processes
    Depending on system implementation, a child process may receive some amount of shared resources with its parent. Child processes may or may not be limited to a ...
  8. [8]
    CS 537 Notes, Section #3A: Processes and Threads - cs.wisc.edu
    The memory that a process can access is called its address space. In all modern operating systems, each process has a separate address space and the OS enforces ...
  9. [9]
    [PDF] COS 318: Operating Systems Processes and Threads - cs.Princeton
    ○ Processes do not usually share memory. ○ Process context switch page table and other memory mechanisms. ○ Threads in a process share the entire address space.
  10. [10]
    CS360 Lecture notes -- Fork - UTK-EECS
    It creates a new process which is a copy of the calling process. That means that it copies the caller's memory (code, globals, heap and stack), registers, and ...
  11. [11]
    getppid
    ### Summary of getppid Function
  12. [12]
    signal(7) - Linux manual page - man7.org
    A child created via fork(2) inherits a copy of its parent's signal dispositions. During an execve(2), the dispositions of handled signals are reset to the ...
  13. [13]
    systemd(1) - Linux manual page - man7.org
    systemd is a system and service manager for Linux operating systems. When run as first process on boot (as PID 1), it acts as init system that brings up and ...
  14. [14]
    getrlimit(2) - Linux manual page
    ### Summary: Inheritance of Resource Limits (rlimit) by Child Processes in Linux
  15. [15]
    [PDF] The Compatible Time-Sharing System - People | MIT CSAIL
    In fact, it has been a goal to enhance and simplify the process ~f sub-system writing by supplying a framework that is highly modul~r and which encourages.<|control11|><|separator|>
  16. [16]
    [PDF] INTRODUCTION AND OVERVIEW OF THE MULTICS SYSTEM
    1. the ability to have one process spawn other processes which run asynchronously on several processors (thus improving the real-time response of ...
  17. [17]
    None
    Below is a merged summary of process/task creation in Burroughs MCP (B5700, 1960s-1972 context), consolidating all provided segments into a single, comprehensive response. To retain maximum detail and ensure clarity, I’ve organized the information into a structured format, including a table where appropriate to handle dense data efficiently. The response preserves all key points, contexts, and references from the original summaries.
  18. [18]
    Programming systems and languages 1965-1975
    In the mid 1960's time sharing was the central con- cept of most operating system research projects. This emphasis was reinforced by the funding agencies of the.
  19. [19]
    Unix and Multics
    Jul 10, 2025 · As described below, Bell Labs left the Multics project in March 1969. MIT started providing Multics service to customers in October 1969, and ...
  20. [20]
    [PDF] The Evolution of the Unix Time-sharing System* - Nokia
    This paper presents a brief history of the early development of the Unix operating system. It concentrates on the evolution of the file system, the process- ...
  21. [21]
    [PDF] The UNIX Time- Sharing System - Berkeley
    When a pro- cess is created by the fork primitive, it inherits not only the core image of its parent but also all the files currently open in its parent, ...
  22. [22]
    The UNIX System -- History and Timeline
    1971, First Edition, It had a assembler for a PDP-11/20, file system, fork(), roff and ed. It was used for text processing of patent documents. ; 1973, Fourth ...
  23. [23]
    Copy on Write - GeeksforGeeks
    May 15, 2020 · Copy on Write or simply COW is a resource management technique. One of its main use is in the implementation of the fork system call in which it shares the ...
  24. [24]
    fork
    The fork() function creates a new process. The new process (child process) is an exact copy of the calling process (parent process) except as detailed below.
  25. [25]
    Basic Shell Job Control in Unix - Edd Mann
    Jan 31, 2014 · Master Unix Shell Job Control to manage background processes and ... Below is an example of sending a copy command to a background process.
  26. [26]
    posix_spawn
    POSIX ™ is a Trademark of The IEEE. Copyright © 2001-2018 IEEE and The Open Group, All Rights Reserved [ Main Index | XBD | XSH | XCU | XRAT ]. <<< Previous ...
  27. [27]
    CreateProcessAsUserA function (processthreadsapi.h) - Win32 apps
    Feb 8, 2023 · Creates a new process and its primary thread. The new process runs in the security context of the user represented by the specified token.
  28. [28]
    _spawn, _wspawn Functions | Microsoft Learn
    Jan 31, 2023 · The _spawn functions each create and execute a new process. They automatically handle multibyte-character string arguments as appropriate.Remarks · Arguments for the spawned...
  29. [29]
    posix_spawn(3) - Linux manual page - man7.org
    The posix_spawn() and posix_spawnp() functions are used to create a new child process that executes a specified file. These functions were specified by POSIX to ...Missing: IEEE | Show results with:IEEE
  30. [30]
    execve(2) - Linux manual page
    ### Summary of execve(2) from https://man7.org/linux/man-pages/man2/execve.2.html
  31. [31]
    getrlimit(2) - Linux manual page
    ### Summary: How setrlimit Works in Child Processes and Inheritance
  32. [32]
    wait(2) - Linux manual page
    ### Summary on Zombie Processes, Parent Synchronization, and Race Conditions
  33. [33]
    vfork(2) - Linux manual page - man7.org
    Historic description Under Linux, fork(2) is implemented using copy-on-write pages, so the only penalty incurred by fork(2) is the time and memory required ...<|separator|>