Process group
In POSIX-conformant operating systems, a process group is a collection of one or more processes that share a common process group identifier (PGID), enabling related processes to be signaled collectively.[1] Each process belongs to exactly one process group, identified by a positive integer PGID, and a newly created process inherits the PGID of its parent by default.[1] The process group leader is the process whose PID equals the PGID. The leader may exit before other members, leaving the group leaderless.[2] Process groups play a central role in job control and inter-process communication, particularly for handling signals such as SIGINT (interrupt) or SIGHUP (hangup), which can be directed to an entire group rather than individual processes.[3] In Unix-like shells, they organize commands like pipelines (e.g.,ls | wc), where all processes in the pipeline form a single group to allow unified suspension, resumption, or termination via terminal input sequences like Ctrl-C.[2] Functions like setpgid() and getpgrp() are used to manage process group membership.
Process groups are further organized into sessions, which are collections of one or more process groups established for job control and tied to a controlling terminal.[1] Within a session, only one process group can be the foreground process group, granting its members privileged access to the terminal for input and output, while background groups are restricted and may receive signals like SIGTTIN or SIGTTOU upon attempting terminal access.[3] This structure supports features like job suspension (Ctrl-Z) and resumption (fg or bg commands), enhancing multitasking in interactive environments.[2]
Definition and Basics
Definition
A process group is a collection of one or more related processes in an operating system that share the same process group identifier (PGID), a unique positive integer assigned to the group during its lifetime.[1] This identifier enables the system to treat the processes as a cohesive unit, distinct from their individual process identifiers (PIDs). Each process belongs to exactly one process group, and newly created processes inherit the PGID of their parent unless explicitly reassigned.[4] The primary purpose of a process group is to facilitate collective management of related processes, particularly by allowing signals and other operations to be directed to all members simultaneously. This supports coordinated control, such as suspending or terminating an entire group in response to user input, without needing to address each process individually.[1] Unlike managing single processes, which operate independently, process groups enable job-like behavior—treating multiple cooperating tasks as a single entity—without requiring direct inter-process communication mechanisms like pipes or shared memory.[4] Process groups form part of a broader hierarchy, where multiple groups can belong to a single session, a higher-level container for related activities. A representative example is a shell command like[cc](/page/CC) -c foo.c, which spawns multiple processes (preprocessor, compiler, assembler) that join the same process group, allowing them to be controlled together, such as by interrupting the entire compilation with Ctrl+C.[4]
Key Attributes
A process group is identified by a unique positive integer known as the process group ID (PGID), which remains associated with the group throughout its lifetime and is not reused by the system until the group ceases to exist.[5] By convention, the PGID is typically set to the process ID (PID) of the process group leader, defined as the process whose PID equals the group's PGID.[6] Processes become members of a process group either upon creation, by inheriting the PGID of their parent process during a fork operation, or through explicit reassignment to another existing group within the same session.[7] This inheritance ensures that child processes start in the same group as their creator, and the PGID is preserved even after an execve call replaces the process image.[8] Process groups exhibit mutability in their composition: they can be created when a process sets its own PGID to match its PID, allowing it to become the leader of a new group, or processes can join an existing group via reassignment functions, provided the target group belongs to the same session.[9] A group dissolves implicitly when its last member process terminates. The scope of a process group is confined to a single session, meaning all member processes must belong to the same session and cannot be reassigned across session boundaries, which enforces isolation for job control and resource management.[8] This limitation ensures that PGIDs are used effectively for targeted operations, such as delivering signals to all processes within the group.[9]Hierarchy and Relationships
Relation to Processes and Sessions
In Unix-like systems adhering to the POSIX standard, each process belongs to exactly one process group, forming a fundamental layer in the process hierarchy that enables coordinated management of related processes.[10] A process group is defined as a collection of processes sharing the same process group ID (PGID), which serves as a unique identifier within the system.[10] Upon creation via thefork() system call, a child process inherits the PGID of its parent, ensuring it initially joins the parent's process group.[2]
Sessions provide a higher-level abstraction in this hierarchy, encompassing one or more process groups that share a common session ID (SID).[11] A session is established for job control purposes and typically includes a controlling terminal accessible only to processes within it, preventing process groups from crossing session boundaries.[11] All members of a given process group are inherently part of the same session, creating a two-level structure where individual processes are nested within groups, and groups are nested within sessions.[11] New processes inherit their session membership from the creating process, maintaining this organizational integrity.[2]
This hierarchical arrangement supports job control by allowing multiple process groups within a single session to operate as distinct foreground or background jobs.[11] The foreground process group gains exclusive input/output access to the session's controlling terminal, while background groups are restricted to prevent interference, facilitating features like job suspension and resumption.[11]
In practice, this structure is evident in interactive shell environments, where each pipeline of commands (e.g., [ls](/page/Ls) | [grep](/page/Grep) pattern) or subshell execution forms a separate process group within the shell's session, enabling independent job management without affecting the entire session.[2]
Process Group Leader and ID
In POSIX-compliant systems, the process group leader is defined as the process whose process ID (PID) equals the process group ID (PGID), typically the initial process that creates the group. This leader process serves as the identifier for the group and coordinates its lifecycle, including the addition of member processes through mechanisms like inheritance or explicit assignment. The leader's role is integral to job control operations, such as signaling and foreground/background management, though it holds no elevated privileges over other group members.[1][9] The PGID is assigned as the PID of the process group leader at the time of group creation, establishing a unique positive integer that identifies the group throughout its existence. Subsequent processes join the group either by inheriting the PGID from their parent during creation or through explicit reassignment, ensuring all members share the same identifier within the session. This assignment convention maintains consistency, as the PGID remains tied to the original leader's PID even as the group evolves.[1][9] Upon termination of the process group leader, the group does not dissolve and continues to exist until its last member process exits or leaves via reassignment. No automatic selection of a new leader occurs; the PGID persists as the original value, and the group retains its identity without a living process holding that specific PID. The process group lifetime, which spans from creation to the departure of all members, underscores this persistence.[1][9] PGIDs must be unique system-wide to prevent conflicts in job control and signaling, but they become available for reuse once the associated process group's lifetime ends. This system-wide scoping ensures that multiple process groups can coexist, including within a single session, without identifier overlap during active periods.[1][9]Implementation
In POSIX and Unix-like Systems
In POSIX-compliant systems, a process group is defined as a collection of one or more processes that share the same process group ID (PGID), which serves as a unique identifier for the group within the session.[12] The POSIX.1 standard (IEEE Std 1003.1) mandates support for process groups, including the ability to query the PGID of a process via functions likegetpgid(), create or join groups using setpgid(), and broadcast signals to all members of a group, such as through kill() with a negative PGID argument.[12][9] This mechanism enables coordinated control of related processes, particularly for job control and signaling, without altering the fundamental process model.[9]
In Unix-like operating systems such as Linux, BSD variants, and Solaris, process groups are implemented at the kernel level to enforce session-based organization and isolation. In the Linux kernel, process groups are managed through the struct pid data structure, which encapsulates identifiers for tasks, groups, and sessions, allowing the kernel to track membership and propagate signals efficiently across group members. FreeBSD, a BSD derivative, inherits process group semantics from early Unix, where each process structure includes a PGID field inherited from the parent, enabling terminal access control and signal distribution to grouped processes for job management.[13] Similarly, in Oracle Solaris, the kernel maintains process group linkages within session structures to support event notification chains, particularly for signals and terminal interactions, ensuring that grouped processes operate cohesively under a shared controlling terminal.
Key constraints in POSIX and Unix-like implementations prevent cross-session intermingling: a process can only join or create a process group within its own session, as setpgid() restricts the target PGID to values valid in the current session, and processes from different sessions cannot share a PGID.[9] Additionally, each session associates with at most one controlling terminal, limiting terminal input/output to a single foreground process group while background groups face restrictions on interactive access to avoid interference.[1]
Modern extensions in Linux integrate process groups with namespaces, particularly the PID namespace, which isolates PGIDs and process visibility within containerized environments like Docker, allowing independent group management per namespace without altering core POSIX mechanics such as signal broadcasting or session isolation. This enhances scalability in virtualized setups while preserving the original group semantics for job control.[14]
Equivalents in Non-POSIX Systems
In Windows operating systems, job objects serve as an analogous mechanism to process groups, enabling the management of multiple processes as a single unit for enforcing resource limits, such as working set size and process priority, and for coordinated termination.[15] Job objects are created using the CreateJobObject function and processes are assigned to them via AssignProcessToJobObject, allowing control over attributes like security descriptors and limits that affect all associated processes.[15] Unlike Unix process groups, which primarily facilitate signal broadcasting to all members, Windows job objects lack native support for direct signal distribution and instead emphasize resource containment and termination policies, such as killing all processes upon job closure.[15] In real-time operating systems (RTOS) outside strict POSIX compliance, equivalents vary. QNX, while POSIX-like due to its microkernel architecture, supports process groups as part of its process management, where the procnto manager handles attributes including process IDs and groups for creation, destruction, and control.[16] It also provides application groups to aggregate processes for unified control and isolation.[17] In contrast, VxWorks primarily operates with tasks as the basic execution units rather than traditional processes, lacking native process groups; instead, it relies on custom mechanisms like VxPOD for grouping and isolating subsystems through allocation of memory, time windows, and system objects.[18] Non-POSIX systems generally prioritize resource control and security isolation over signal handling in their grouping constructs; for instance, Windows job objects establish security boundaries and limit resource usage across processes without the broadcast semantics central to Unix process groups.[15] Cross-platform libraries such as Cygwin emulate Unix process groups on Windows by providing a POSIX-compatible API layer through its DLL, enabling fork, exec, and group management behaviors on top of the Windows kernel.[19]Uses and Applications
Signal Delivery
In Unix-like operating systems conforming to POSIX, process groups facilitate the delivery of signals to multiple related processes simultaneously, enabling coordinated control such as interruption or termination of job pipelines. Thekill() function sends a signal to an entire process group by specifying a negative process group ID (PGID) as the pid argument, where the absolute value of pid identifies the target group; for example, kill(-PGID, sig) broadcasts the signal to all member processes for which the sender has permission.[20] This mechanism is particularly useful for signals like SIGINT, which is generated by the terminal driver upon receiving the INTR character (typically Ctrl+C) and delivered to all processes in the foreground process group associated with the controlling terminal, provided the ISIG flag is set in the terminal attributes.
A key use case arises in terminal interactions, where the foreground process group ensures that user-initiated interrupts affect an entire command or job, such as a shell pipeline, rather than isolated processes. This targeted broadcasting prevents scenarios where child processes might otherwise evade signals intended for the group, maintaining orderly job execution. Additionally, in the context of orphaned process groups—those whose session leader has terminated—the kernel automatically sends a SIGHUP signal to all members upon certain events, like terminal closure, compelling cleanup and preventing detached processes from persisting without access to the controlling terminal.[21]
Exceptions apply to certain signals that can be directed to process groups via the same kill(-PGID, sig) syntax, notably SIGKILL, which unconditionally terminates all group members, and SIGSTOP, which suspends them; unlike most signals, these cannot be caught, blocked, or ignored by the recipients.[20] Delivery order within the group is implementation-defined and not guaranteed by POSIX, though signals are sent to each eligible process individually after permission checks.[20]
Security restrictions ensure controlled access: a process may only signal a foreign process group if it possesses superuser privileges or if its effective or real user ID matches the real or saved set-user-ID of each target process in the group.[20] No such user ID verification occurs for SIGCONT within the same session, allowing resumption of stopped processes without additional checks.[20]
Job Control and Management
In Unix-like operating systems, job control refers to the mechanisms that enable a shell to manage multiple concurrent jobs, where each job consists of one or more related processes grouped into a process group. This allows users to execute commands in the foreground, suspending or terminating them as needed, while running other commands in the background without interrupting the primary interaction. The shell assigns a unique process group to each job, facilitating collective operations such as signal delivery to all member processes.[4] A key aspect of job control is the distinction between foreground and background jobs within a session tied to a controlling terminal. The foreground process group receives exclusive access to the terminal for input and output, while background groups are restricted to prevent interference; attempts by background processes to read from the terminal trigger the SIGTTIN signal, typically stopping the process, and writes may trigger SIGTTOU if the terminal is in TOSTOP mode. This enforcement is handled by the terminal driver, ensuring that only the foreground job can interact directly with the user. Shell utilities likefg (foreground) and bg (background) manipulate these assignments by changing the foreground process group via system calls such as tcsetpgrp(), which sets the terminal's foreground group ID to that of the target process group.[22][4]
Job management also involves monitoring and controlling job states, including running, stopped, or completed. The shell tracks these via the jobs utility, which reports the status of all active jobs in the current session, displaying process group leaders and their conditions (e.g., "Running" or "Stopped"). Signals like SIGINT (from Ctrl+C) or SIGTSTP (from Ctrl+Z) are directed to the entire foreground process group, allowing unified suspension or termination. Orphaned process groups—those whose session leader has terminated—receive special handling, such as ignoring certain job control signals to prevent indefinite stops. This framework, defined in POSIX.1, supports interactive shells in multitasking environments by integrating process groups with terminal I/O and signal protocols.[23][22]
System Calls and APIs
Creating and Managing Groups
In POSIX-compliant systems, process groups are created and managed primarily through thesetpgid() and setsid() system calls, which allow processes to join existing groups, form new ones, or establish sessions with dedicated process groups.[9] The setpgid() function assigns a process to a specified process group ID (PGID) within the same session, enabling either the joining of an existing group or the creation of a new one if the PGID matches the process ID (PID) of the target process.[9] This call is typically invoked by shell implementations, such as those for job control, before executing a new program via exec() to ensure the child process inherits the desired group membership.[8] However, setpgid() fails if the target process is a child of the caller that has already executed an exec() function, preventing group changes after the process image is replaced.[9]
The setsid() function creates a new session and simultaneously establishes a new process group within that session, with the calling process becoming the leader of both. It succeeds only if the caller is not already a process group leader, ensuring no overlap with existing groups, and detaches the process from any controlling terminal. This mechanism is essential for daemon processes or background jobs that require isolation from the parent's session.[24] Additional process groups can then be formed within the new session using setpgid().
For retrieving the PGID of the calling process during management operations, the POSIX getpgrp() function provides a simple interface equivalent to getpgid(0), returning the current process group's ID without arguments. This call always succeeds and aids in verifying group assignments before further modifications.
Process groups are implicitly managed by the kernel and dissolve automatically when the last member process terminates, as there is no explicit system call to delete a group. Once empty, the PGID becomes available for reuse, maintaining system efficiency without manual intervention.
Querying Group Information
Process group information can be queried using specific system calls in POSIX-compliant systems, allowing applications and administrators to retrieve details about process affiliations without altering the group structure. These functions provide essential data for understanding process hierarchies, particularly in multi-process environments where coordination is key.[12] Thegetpgid() function retrieves the process group ID (PGID) of a specified process identified by its process ID (pid); if pid is zero, it returns the PGID of the calling process itself. This function is useful for determining group membership across related processes, returning -1 on error with errno set to indicate the failure reason, such as if the process does not exist.[12]
For terminal-related queries, the tcgetpgrp() function obtains the PGID of the foreground process group associated with a controlling terminal specified by file descriptor fd. It returns this PGID value directly or -1 on error, enabling processes—even those in background groups—to check the current foreground status without permission restrictions beyond file access.[25]
Related to process groups, the getsid() function retrieves the session ID of the process specified by pid, which is the PGID of the session leader process containing the target; if pid is zero, it returns the session ID of the calling process. This provides context on broader session containment, as detailed in the relation to processes and sessions.[26]
In debugging and system monitoring, these query functions underpin tools like the ps command, which displays PGID alongside other process details when invoked with options such as -o pid,pgid or the BSD-style j format, facilitating the visualization of process group hierarchies for troubleshooting and resource management.[27]