Recent from talks
Nothing was collected or created yet.
Job control (Unix)
View on WikipediaIn a Unix or Unix-like operating system, job control refers to controlling a process group as a job via a shell.[1] Control features include suspend, resume, and terminate, and more advanced features can be performed by sending a signal to a job. Job control allows a user to manage processing in the Unix-based multiprocessing environment, and is distinct from general computing job control.
Job control was first implemented in the C shell by Jim Kulp,[2] then at IIASA in Austria, making use of features of the 4.1BSD kernel. The KornShell, developed at Bell Labs, adopted it and it was later incorporated into the SVR4 version of the Bourne shell, and exists in most modern Unix shells.
Job
[edit]A job encompasses all of the processes that start for the handling of a shell command line. A simple command line may start just one process, but a command line may result in multiple processes since a process can create child processes, and a command line can specify a pipeline of multiple commands. For example, the following command line selects lines containing the text "title", sorts them alphabetically, and displays the result in a terminal pager: grep title somefile.txt | sort | less. This creates at least three processes: one for grep, one for sort, and one for less. Job control allows the shell to control these processes as one entity.
Job ID
[edit]A job is identified by a numeric job ID, a.k.a. job number which is classified as a handle since it is an abstract reference to a resource (a process group). An ID value, prefixed with %, can be used with a job control command to specify a job. The special references %% and %+ refer to the default job; the one that would be selected if none specified.[3] Bash documentation refers to a reference (starting with %) as a jobspec (short for job specification).[4]
Job control ID values are typically only used in an interactive shell. In scripting, PGID values are used instead, as they are more precise and robust, and indeed job control is disabled by default in a bash script.
Foreground/background
[edit]By default, a job runs in the foreground where it uses interactive input and output. The user enters a command line and interacts with the processes but cannot issue another command until the current job terminates. Many operations (i.e. listing files) are relatively quick so the user can wait for a response with little down time and some operations (i.e. editing) require interaction that is only possible via a foreground job. But, if interaction is not required and the operation prevents access to the shell for a long time, the user may want to run it in the background – where the processes cannot access interactive input but the user can perform other foreground operations while the background job runs concurrently. By default background jobs output to the interactive output stream which results in the interleaving of output from the foreground and background jobs although a user may redirect output for a background job to prevent this.
Control
[edit]POSIX specifies the user interface to job control – modeled on the Korn shell.[5]. The commands are typically implemented as shell builtins; not separate programs.
- Start in background
- If a command line ends with
&, then the job starts in the background. - Pause foreground job
- The foreground job can be paused by pressing Ctrl+ Z. In this state, a job can be resumed in the background via
bgor resumed in the foreground viafg. - Command
fg - Command
fg(short for foreground) moves background job to the foreground; either the job specified or the one most recently added to the background if none specified. When the foreground job is paused (via Ctrl+ Z), then this command resumes that job. - Command
wait - Command
waitpauses the interactive session until the specified background jobs complete or for all background jobs of the active shell if none specified.[6] - Command
bg - Command
bg(short for background) moves the paused foreground job to the background and resumes it. - Command
jobs - Command
jobsreports information about each background job including ID, command line and running status (stopped or running).
Signals
[edit]The interprocess communication of job control is implemented via signals.
Typically, a shell maintains information about background jobs in a job table. When an interactive session ends (i.e. user logs out), the shell sends signal SIGHUP to all jobs, and waits for the process groups to exit before terminating itself. Some shells provide a non-POSIX command disown that removes a job from the job table. The process group becomes an orphan. The shell will not send it SIGHUP, nor wait for it to terminate. This is one technique for enabling a process as a daemon; owned direclty by the root process init. The POSIX command nohup provides an alternate way to prevent a job from being terminated by the shell.
Suspending the foreground job (via Ctrl +Z) sends signal SIGTSTP (terminal stop) to the processes of the group. By default, this signal causes a process to pause so that the shell can resume. However, a process can ignore the signal. A process can also be paused via signal SIGSTOP (stop), which cannot be ignored.
When the user presses Ctrl +C, the shell sends signal SIGINT (interrupt) to each foreground job process, which defaults to terminating it, though a process can ignore the signal.
When a stopped job is resumed (via bg or fg), the shell redirects Input/output and resumes it by sending signal SIGCONT to it.
A background process that attempts to read from or write to its controlling terminal is sent signal SIGTTIN (for input) or SIGTTOU (for output). These signals stop the process by default, but they may also be handled in other ways. Shells often override the default stop action of SIGTTOU so that background processes deliver their output to the controlling terminal by default.
In bash, the kill builtin (not /bin/kill) can signal jobs by ID as well as by process group ID. Sending a signal to a job sends it to each process of the group. kill can send any signal to a job; however, if the intent is to rid the system of the processes, the signals SIGKILL and SIGTERM (the default) are probably the most applicable.
References
[edit]- ^ IEEE Std 1003.1-2001, Section 3.201, Job
- ^ Foreword by Bill Joy in Anderson, Gail; Paul Anderson (1986). The UNIX C Shell Field Guide. Prentice-Hall. p. xvii. ISBN 0-13-937468-X.
- ^ IEEE Std 1003.1-2001, Section 3.203, Job Control Job ID
- ^ 7.1 Job Control Basics
- ^ – Shell and Utilities Reference, The Single UNIX Specification, Version 5 from The Open Group; – Shell and Utilities Reference, The Single UNIX Specification, Version 5 from The Open Group.
- ^ Kerrisk, Michael (Feb 2, 2025). "wait(1p) — Linux manual page". man7.org. Retrieved May 13, 2025.
Further reading
[edit]- Marshall Kirk McKusick and George V. Neville-Neil (2004-08-02). "FreeBSD Process Management: Process Groups and Sessions". The Design and Implementation of the FreeBSD Operating System. Addison Wesley. ISBN 0-201-70245-2.
External links
[edit]- "Job Control", Bash Reference Manual
Job control (Unix)
View on GrokipediaOverview
Definition and Purpose
Job control is a mechanism in Unix-like operating systems that enables shells to manage groups of processes, referred to as jobs, by allowing users to suspend, resume, and switch between foreground and background execution modes.[6] This feature relies on the shell's ability to organize related processes into process groups and coordinate their access to the controlling terminal through system calls likesetpgid() and tcsetpgrp().[7][8]
The primary purpose of job control is to facilitate multitasking within interactive shells by decoupling process input/output from the terminal, thereby preventing long-running tasks from blocking user input and allowing asynchronous execution.[6] It addresses limitations in early Unix shells, such as the original Bourne shell, which could only execute one process interactively at a time, by introducing the ability to run multiple jobs concurrently and manage them dynamically. This improves user productivity in command-line environments, enabling efficient oversight of computational tasks without interrupting shell interaction.[6]
For instance, a user can initiate a time-intensive compilation by appending & to the command, such as make &, which launches the job in the background and returns control to the shell prompt immediately for further input.[9]
Historical Development
Job control originated in the Unix ecosystem with its implementation in the C shell (csh), developed by Bill Joy at the University of California, Berkeley. Jim Kulp, working at the International Institute for Applied Systems Analysis (IIASA), contributed the initial job control features, including the ability to run processes in the background and suspend jobs using signals like SIGTSTP, which were integrated into 4.1BSD, released in June 1981.[4] This innovation built on the 4.1 BSD kernel's support for process groups and session management, providing users with early mechanisms for handling concurrency through foreground and background execution modes.[10] During the 1980s, job control saw broader adoption across Unix shells. The KornShell (ksh), created by David Korn at AT&T Bell Laboratories and first announced in 1983, extended these capabilities with enhanced scripting support and command-line editing while maintaining compatibility with the Bourne shell.[11] Later, the Bourne shell itself incorporated job control in System V Release 4 (SVR4), released by AT&T in October 1988, marking a significant step toward standardization in commercial Unix variants.[12] The release of 4.2 BSD in August 1983 further popularized job control, as its widespread distribution in academic and research institutions drove adoption among developers and users.[4] Job control was formalized in the POSIX.1-1990 standard (IEEE Std 1003.1-1990), where it was defined as an optional feature requiring support for process groups, job control signals, and shell builtins likebg and fg. Subsequent revisions, such as POSIX.1-2001, refined the specification, particularly improving signal handling to ensure consistent behavior across job states and terminals. Job control became a required feature in POSIX.1-2008. By the 1990s, job control had become ubiquitous in Unix-like operating systems, including Linux distributions, macOS (via BSD heritage), and various BSD derivatives, though some early commercial Unix implementations from vendors like AT&T initially resisted full integration due to the perceived complexity of managing process groups and signals.[4]
Core Concepts
Jobs
In Unix job control, a job is defined as a set of one or more processes that originate from a single shell command line, organized into a process group sharing a common process group ID (PGID).[13][14] This structure allows the shell to treat the entire group as a cohesive unit for control purposes, such as signal delivery to all member processes simultaneously.[13] The shell forms a job by creating a new process group when invoking the command, typically using system calls likesetpgid to assign the shared PGID to all processes in the group.[13] For instance, in a pipeline such as cmd1 | cmd2, the shell forks the processes and connects them via pipes while placing them under the same PGID, ensuring they operate as a linked unit.[9] The job leader is the initial process in the group—often the first command executed—whose process ID (PID) matches the PGID, serving as the identifier for the entire job.[14][13]
Jobs are maintained on a per-shell basis, with each interactive shell tracking its own job table independently of other shells or the kernel's global process management, which relies on unique PIDs for individual processes.[15] This shell-specific organization enables isolated management within a session, separate from system-wide oversight.[13]
A representative example is the command grep foo file | sort, which spawns a job comprising the grep and sort processes in a single process group, with the PGID set to the PID of the grep process to facilitate collective handling.[15][14]
Job Identifiers
In Unix job control, jobs are referenced using job identifiers, which provide a convenient way for users to specify particular jobs in shell commands. These identifiers are assigned by the shell to track and manage groups of processes treated as a single unit.[16] The primary form of job identifiers consists of sequential numeric IDs, starting from 1 and incrementing for each new job launched in the shell session; for example, the first background job receives [17], the next [18], and so on. The current job—the most recently accessed or the default for operations—is denoted by %+ or %%, while the previous job is indicated by %-. These numeric and symbolic forms enable quick referencing without needing to recall process details.[16] POSIX-standard identifiers also include %string, which refers to the job whose command line begins with the specified string, and %?string, which matches any job containing that string anywhere in its command; shells like Bash implement these along with further extensions for more flexible matching, such as %PGID, which directly uses a process group ID to identify the job. These alternatives allow users to select jobs based on descriptive attributes rather than just numbers, though they may fail if multiple jobs match ambiguously.[1] Job identifiers are designed primarily for use in interactive shells, where users manually manage multiple concurrent processes; in non-interactive scripts, shells typically bypass job identifiers in favor of direct process group IDs (PGIDs) through system calls such as waitpid() for waiting on or manipulating specific groups.[9] These identifiers are scoped to the individual shell instance and reset upon shell restart or when the job table is cleared, ensuring no carryover between sessions. The number of active jobs is constrained by the shell's internal job table size, which varies by implementation but is generally sufficient for typical interactive use, limited ultimately by system resources rather than a fixed cap.[1]Execution Modes
Foreground Processes
In Unix job control, a foreground process executes with exclusive access to the controlling terminal, receiving standard input (stdin), standard output (stdout), and standard error (stderr) directly from it, while the shell waits for the process to complete, blocking further command input.[9] This synchronous execution ensures that interactive operations, such as reading user keystrokes or displaying prompts, occur seamlessly without interference.[19] By default, commands entered in the shell run as foreground processes unless explicitly suffixed with an ampersand (&), which designates them for background execution.[9] This default is crucial for programs requiring ongoing user interaction, like the vi editor, where real-time input and output are necessary for editing sessions.[9]
Foreground processes inherit the terminal's configuration attributes from the shell, including echo mode (which echoes typed characters) and canonical mode (which buffers input until a newline).[19] Only one process group can occupy the foreground for a terminal at a time, preventing concurrent access and maintaining orderly I/O handling.[19]
For example, executing top as a foreground process provides continuous, real-time updates of system resource usage on the terminal screen, leveraging direct stdout access until the user interrupts it.[19] In contrast to background processes, this mode blocks the shell prompt, emphasizing its suitability for terminal-centric tasks.[9]
Background Processes
In Unix job control, background processes enable asynchronous execution of commands, allowing users to initiate non-interactive tasks without tying up the shell's terminal interface. To start a background process, the shell's command language uses the ampersand (&) control operator appended to the command, as in sleep 100 &. This launches the command in a subshell environment within a new process group designated as background, detaching it from direct terminal control; the shell immediately returns the prompt and does not wait for completion, storing the process ID (PID) of the job's leader process in the special parameter $! for access until the job ends or is replaced.[9]
Standard output (stdout) and standard error (stderr) for background processes remain connected to the terminal by default, potentially causing their output to interleave with subsequent shell prompts or commands unless explicitly redirected, such as to /dev/null via > /dev/null 2>&1. In contrast, standard input (stdin) is not actively connected to the terminal for interactive use; attempts by the background process to read from it trigger a SIGTTIN signal to the entire process group, typically stopping the job unless the signal is ignored or blocked. This I/O behavior supports safe detachment while preventing unintended terminal interference, though redirection is recommended for clean operation.[20][21]
Background execution is non-blocking, permitting multiple such jobs to run concurrently alongside interactive shell use, which proves valuable for resource-intensive operations like file backups or simulations that do not require real-time user input. Upon initiation, many shells, including widely used implementations like Bash, notify the user by printing a message to standard error in a format such as [1] 1234, where 1 denotes the job number and 1234 the PID of the leader process, facilitating quick identification and monitoring.[9][22]
Management Operations
Listing and Monitoring Jobs
In Unix job control, the primary command for listing and monitoring active jobs within the current shell session is thejobs builtin utility, which displays the status of stopped or background jobs maintained by the shell.[23] This command provides a shell-specific view of job states, helping users track processes without needing system-wide process listings.[23] The shell tracks these jobs through an internal job table that records job identifiers, process group leaders, and status changes, such as completion or suspension, allowing for efficient oversight in interactive environments.
The default output of jobs follows a standardized format: [%d] %c %s %s\n, where %d is the job number (a small integer assigned sequentially), %c indicates the current job with a + (default for foreground commands like fg), the previous job with a -, or a space for others, %s denotes the state, and the final %s is the command line.[23] For example, running jobs might produce:
[1] Running sleep 100 &
[2]- Stopped(SIGTSTP) vim file.txt
[3]+ Done echo "Hello"
[1] Running sleep 100 &
[2]- Stopped(SIGTSTP) vim file.txt
[3]+ Done echo "Hello"
-l option includes process group IDs (PGIDs) alongside the default information, listing each process in multi-process jobs separately for detailed tracking, such as [1] 1234 Running sleep 100 &.[23] Conversely, -p outputs only the process IDs (PIDs) of job leaders, one per line, facilitating scripting or integration with other tools like kill for targeted operations.[23] In shells like Bash, additional flags such as -n restrict output to jobs with unreported status changes, -r for running jobs only, and -s for stopped jobs, providing filtered views without altering the core POSIX behavior.[24]
While jobs offers a concise, shell-centric snapshot, users often complement it with the ps command for broader resource monitoring, using PIDs or PGIDs from jobs -l or -p to query detailed metrics like CPU usage or memory.[25] For instance, jobs -p | xargs ps -o pid,ppid,cmd reveals parent-child process hierarchies and command arguments beyond the shell's job table.[26] This integration underscores jobs as a lightweight tool for job visibility, distinct from system-level process management.[23]
Manipulating Job States
In Unix job control, suspending a foreground job is achieved by pressing Ctrl+Z, which generates the SIGTSTP signal and transitions the job to a stopped state, allowing the shell to regain control of the terminal.[21] This mechanism is part of the POSIX-defined terminal interface, where SIGTSTP serves as the default signal for terminal-generated stops, enabling interactive management without terminating the process.[21] Resuming and switching job states rely on thefg and bg builtins, which operate on jobs identified by specifiers such as %n (job number n), %% (current job), or %- (previous job), often determined via the jobs command.[23] The fg command brings a specified job to the foreground, resuming it if stopped, and places it in direct terminal interaction mode; if no job is specified, it defaults to the most recent job.[27] Conversely, the bg command resumes a stopped job in the background, allowing concurrent execution without terminal attachment; it has no effect on already-running background jobs and also defaults to the current job if unspecified.[28] These operations require job control to be enabled in the shell, typically via set -m in POSIX-compliant environments.[29]
The wait builtin suspends shell execution until the specified job completes, blocking the terminal and returning the job's exit status upon termination; without arguments, it waits for all jobs.[30] For a job identifier like %1, wait %1 yields the exit code (0-255) or signal number if terminated abnormally, facilitating scripted dependency handling in pipelines or asynchronous tasks.[30] This POSIX utility ensures reliable synchronization, with an exit status of 127 for unknown jobs.[30]
In Bash and compatible shells, the disown builtin removes a job from the shell's active table, preventing it from receiving the SIGHUP signal upon shell exit or logout, thus allowing detached persistence. Invoked as disown %job, it eliminates the job from management (e.g., no longer visible in jobs), but the process continues running independently; the -h option marks it without removal to avoid hangup while retaining table entry. This extension beyond core POSIX supports long-running detached operations, such as in remote sessions.
Supporting Signals
Suspension and Resumption Signals
In Unix job control, the suspension and resumption of processes are primarily managed through specific signals that allow interactive pausing and restarting of job groups without terminating them. The SIGTSTP signal, defined in POSIX as a terminal stop signal, is generated when a user presses Ctrl+Z in an interactive shell, causing the suspension of all processes in the foreground process group by halting their execution.[21][31] This signal can be caught or ignored by applications, enabling custom handling, but its default action is to stop the processes, facilitating temporary interruption for job management.[21] Complementing SIGTSTP is the SIGSTOP signal, which unconditionally stops the execution of processes in a job group and cannot be caught, blocked, or ignored.[21] POSIX specifies SIGSTOP's default action as stopping the process, though it is not typically generated by keyboard input or used in standard shell job control operations.[21] Additional suspension signals include SIGTTIN and SIGTTOU, which are sent to background processes attempting to read from (SIGTTIN) or write to (SIGTTOU) the controlling terminal. These signals stop the processes by default to prevent them from interfering with the foreground job's terminal access, ensuring orderly job control. Like SIGTSTP, they can be caught or ignored, but interactive shells often ignore them to avoid unintended suspension.[21][31] Resumption is handled by the SIGCONT signal, which restarts processes previously stopped by SIGTSTP, SIGTTIN, SIGTTOU, or SIGSTOP, continuing their execution from the point of suspension while ignoring the signal if the processes are already running.[21] In practice, shells send SIGCONT to the entire process group of a suspended job when the fg command brings it to the foreground or the bg command resumes it in the background, ensuring coordinated restart across all related processes.[27][28] These signals are broadcast to the entire process group associated with the job, as required by POSIX for effective interactive control, allowing the shell to manage job states consistently without affecting unrelated processes.[32] POSIX mandates that compliant shells properly interpret and propagate SIGTSTP, SIGTTIN, SIGTTOU, and SIGCONT to support job suspension and resumption, ensuring portability across Unix-like systems.[31]Termination and Interrupt Signals
In Unix job control, termination and interrupt signals provide mechanisms to end processes gracefully or immediately, particularly in interactive shells where user intervention or session changes occur. These signals are part of the POSIX standard and are handled by the kernel, with shells like Bash implementing specific behaviors for foreground and background jobs.[21] The SIGINT signal, defined as the terminal interrupt signal in POSIX, is generated when a user presses Ctrl+C at the controlling terminal. It is sent exclusively to the foreground process group, requesting interruption and typically causing termination unless the process catches or ignores it; this allows users to halt interactive commands without affecting background jobs.[21][1] SIGTERM serves as a polite termination request, also standardized in POSIX with a default action of abnormal termination. Commonly sent via thekill command without a specified signal (e.g., kill <pid>), it enables processes to perform cleanup operations, such as closing files or saving state, before exiting, unlike more forceful signals.[21][33]
SIGHUP, the hangup signal per POSIX, is sent to background jobs when the controlling shell exits, such as during user logout, to notify processes of the terminal disconnection; its default action is termination, potentially leading to abrupt ends for unattended tasks unless the signal is ignored.[21][34]
To protect jobs from SIGHUP during logout, the nohup utility launches processes that ignore this signal by default and redirect output to a file, ensuring continuity even after session closure. Complementing this, the disown builtin in shells like Bash removes jobs from the shell's table, preventing the shell from sending SIGHUP upon exit while allowing the processes to continue as orphans under the init process.[35][34]
Implementation Details
Shell Support and Variations
Bash provides full support for POSIX-compliant job control, including builtins such asjobs, fg, bg, and disown for managing process groups, with extensions like immediate background job notifications via set -b. In interactive shells, job control is enabled by default, but it can be explicitly activated in non-interactive contexts using the set -m option, which turns on the monitor mode to handle suspension and resumption signals properly. This aligns with the POSIX baseline, where job control facilitates asynchronous command execution and signal handling for process states.[22][36][9]
Zsh extends POSIX job control with enhanced features, such as the AUTO_CONTINUE option that automatically sends SIGCONT to stopped jobs removed via disown, and improved job specifications allowing flexible referencing by command strings or patterns. It also supports auto-resumption of suspended jobs through shorthand commands like r for the most recent stopped job, and the notify option for real-time status updates on job completion, making interactive use more intuitive than standard POSIX implementations.[37][38]
Fish shell implements job control in a non-POSIX manner, prioritizing user-friendly syntax over strict compliance, with commands like jobs offering options such as --command to display job names and --pid for process IDs, while omitting headers in non-interactive output for cleaner scripting. Features include straightforward suspension via Control-Z and resumption with disown or backgrounding, but it deviates from POSIX by using distinct syntax for job expansion (e.g., %job_id) and providing built-in support for querying job existence without traditional job control signals in all contexts. This approach enhances usability for interactive sessions, such as automatically handling CPU usage displays that can exceed 100% on multi-processor systems.[39][40]
The C shell (csh) and its successor tcsh were among the original implementers of job control in Unix, introducing commands like fg %job, bg %job, and job specifiers such as %string for referencing processes by name. However, they exhibit limitations in scripting support, lacking robust functions and suffering from awkward I/O redirection, which complicates programmatic manipulation of jobs compared to more modern shells like Bash.[41]
Variations across shells include performance-oriented defaults, such as in dash, where job control is disabled by default in non-interactive modes to improve execution speed, requiring explicit enabling via the -m option or interactive terminal detection. Modern challenges arise in multi-threaded environments, where signal delivery for job control (e.g., SIGCHLD for child process status) can introduce race conditions if not synchronized properly, as signals are delivered to an arbitrary eligible thread, potentially leading to inconsistent job state tracking.[42][43][44]
POSIX Standards and Compliance
The POSIX.1-2008 standard mandates that conforming shells support job control features, including the built-in commandsfg, bg, jobs, and wait, particularly in interactive environments where job control is enabled by default via the monitor mode (set -m). These utilities enable users to manage background and suspended processes: fg brings a specified job to the foreground, bg resumes a suspended job in the background, jobs displays the status of active jobs (including states like running, done, or stopped), and wait suspends the shell until specified background jobs complete. Additionally, signals SIGTSTP (for suspending interactive jobs, typically via Ctrl+Z) and SIGCONT (for resuming suspended jobs) are required for job control operations in interactive shells, with the shell handling their delivery to process groups. While job control was optional under earlier profiles like User Portability Utilities (pre-2008), it is mandatory for full shell conformance since POSIX.1-2008, including in interactive modes.[9][28][27][23][30][45][23]
Compliance with POSIX job control is structured across volumes of the standard: the Shell and Utilities volume (XCU) defines the semantics of job control commands and their behavior in asynchronous lists and interactive sessions, while the Base Definitions volume (XBD) specifies foundational concepts such as process groups, sessions, and job IDs (e.g., %n for job number n or %% for the current job) that underpin process group handling for signaling and terminal control. These definitions ensure that jobs are managed as collections of processes sharing a process group ID, with foreground process groups designated via terminal interfaces.[46][16]
The POSIX.1-2024 standard (Issue 8) further refines job control by requiring shells to report immediately upon the suspension of a foreground job and to save the terminal state when a foreground job is suspended, enhancing consistency in terminal interactions across conforming systems.[47]
For portability, systems lacking full POSIX compliance, such as early Unix variants like Seventh Edition (1979) or pre-4.1BSD releases, do not support job control features, treating background processes synchronously without suspension or resumption capabilities. Conformance can be verified using POSIX test suites, such as those aligned with IEEE Std 1003.1-2024.[48][49]References
Summary of Job Control Basics (Bash Reference Manual)
- The job control features provided by bg, fg, and jobs are based on the KornShell. The standard developers examined the characteristics of the C shell versions ...
