Hubbry Logo
Process (computing)Process (computing)Main
Open search
Process (computing)
Community hub
Process (computing)
logo
8 pages, 0 posts
0 subscribers
Be the first to start a discussion here.
Be the first to start a discussion here.
Contribute something
Process (computing)
Process (computing)
from Wikipedia
Program vs. Process vs. Thread
Scheduling, Preemption, Context Switching

In computing, a process is the instance of a computer program that is being executed by one or many threads. There are many different process models, some of which are light weight, but almost all processes (even entire virtual machines) are rooted in an operating system (OS) process which comprises the program code, assigned system resources, physical and logical access permissions, and data structures to initiate, control and coordinate execution activity. Depending on the OS, a process may be made up of multiple threads of execution that execute instructions concurrently.[1][2]

While a computer program is a passive collection of instructions typically stored in a file on disk, a process is the execution of those instructions after being loaded from the disk into memory. Several processes may be associated with the same program; for example, opening up several instances of the same program often results in more than one process being executed.

Multitasking is a method to allow multiple processes to share processors (CPUs) and other system resources. Each CPU (core) executes a single process at a time. However, multitasking allows each processor to switch between tasks that are being executed without having to wait for each task to finish (preemption). Depending on the operating system implementation, switches could be performed when tasks initiate and wait for completion of input/output operations, when a task voluntarily yields the CPU, on hardware interrupts, and when the operating system scheduler decides that a process has expired its fair share of CPU time (e.g, by the Completely Fair Scheduler of the Linux kernel).

A common form of multitasking is provided by CPU's time-sharing that is a method for interleaving the execution of users' processes and threads, and even of independent kernel tasks – although the latter feature is feasible only in preemptive kernels such as Linux. Preemption has an important side effect for interactive processes that are given higher priority with respect to CPU bound processes, therefore users are immediately assigned computing resources at the simple pressing of a key or when moving a mouse. Furthermore, applications like video and music reproduction are given some kind of real-time priority, preempting any other lower priority process. In time-sharing systems, context switches are performed rapidly, which makes it seem like multiple processes are being executed simultaneously on the same processor. This seemingly-simultaneous execution of multiple processes is called concurrency.

For security and reliability, most modern operating systems prevent direct communication between independent processes, providing strictly mediated and controlled inter-process communication.

Representation

[edit]
A list of processes as displayed by htop
A process table as displayed by KDE System Guard
A process table as displayed by KDE System Guard

In general, a computer system process consists of (or is said to own) the following resources:

  • An image of the executable machine code associated with a program.
  • Memory (typically some region of virtual memory); which includes the executable code, process-specific data (input and output), a call stack (to keep track of active subroutines and/or other events), and a heap to hold intermediate computation data generated during run time.
  • Operating system descriptors of resources that are allocated to the process, such as file descriptors (Unix terminology) or handles (Windows), and data sources and sinks.
  • Security attributes, such as the process owner and the process' set of permissions (allowable operations).
  • Processor state (context), such as the content of registers and physical memory addressing. The state is typically stored in computer registers when the process is executing, and in memory otherwise.[1]

The operating system holds most of this information about active processes in data structures called process control blocks. Any subset of the resources, typically at least the processor state, may be associated with each of the process' threads in operating systems that support threads or child processes.

The operating system keeps its processes separate and allocates the resources they need, so that they are less likely to interfere with each other and cause system failures (e.g., deadlock or thrashing). The operating system may also provide mechanisms for inter-process communication to enable processes to interact in safe and predictable ways.

Multitasking and process management

[edit]

A multitasking operating system may just switch between processes to give the appearance of many processes executing simultaneously (that is, in parallel), though in fact only one process can be executing at any one time on a single CPU (unless the CPU has multiple cores, then multithreading or other similar technologies can be used).[a]

It is usual to associate a single process with a main program, and child processes with any spin-off, parallel processes, which behave like asynchronous subroutines. A process is said to own resources, of which an image of its program (in memory) is one such resource. However, in multiprocessing systems many processes may run off of, or share, the same reentrant program at the same location in memory, but each process is said to own its own image of the program.

Processes are often called "tasks" in embedded operating systems. The sense of "process" (or task) is "something that takes up time", as opposed to "memory", which is "something that takes up space".[b]

The above description applies to both processes managed by an operating system, and processes as defined by process calculi.

If a process requests something for which it must wait, it will be blocked. When the process is in the blocked state, it is eligible for swapping to disk, but this is transparent in a virtual memory system, where regions of a process's memory may be really on disk and not in main memory at any time. Even portions of active processes/tasks (executing programs) are eligible for swapping to disk, if the portions have not been used recently. Not all parts of an executing program and its data have to be in physical memory for the associated process to be active.

Process states

[edit]
The various process states, displayed in a state diagram, with arrows indicating possible transitions between states

An operating system kernel that allows multitasking needs processes to have certain states. Names for these states are not standardised, but they have similar functionality.[1]

  • First, the process is "created" by being loaded from a secondary storage device (hard disk drive, CD-ROM, etc.) into main memory. After that the process scheduler assigns it the "waiting" state.
  • While the process is "waiting", it waits for the scheduler to do a so-called context switch. The context switch loads the process into the processor and changes the state to "running" while the previously "running" process is stored in a "waiting" state.
  • If a process in the "running" state needs to wait for a resource (wait for user input or file to open, for example), it is assigned the "blocked" state. The process state is changed back to "waiting" when the process no longer needs to wait (in a blocked state).
  • Once the process finishes execution, or is terminated by the operating system, it is no longer needed. The process is removed instantly or is moved to the "terminated" state. When removed, it just waits to be removed from main memory.[1][3]

Inter-process communication

[edit]

When processes need to communicate with each other they must share parts of their address spaces or use other forms of inter-process communication (IPC). For instance in a shell pipeline, the output of the first process needs to pass to the second one, and so on. Another example is a task that has been decomposed into cooperating but partially independent processes which can run simultaneously (i.e., using concurrency, or true parallelism – the latter model is a particular case of concurrent execution and is feasible whenever multiple CPU cores are available for the processes that are ready to run).

It is even possible for two or more processes to be running on different machines that may run different operating system (OS), therefore some mechanisms for communication and synchronization (called communications protocols for distributed computing) are needed (e.g., the Message Passing Interface {MPI}).

History

[edit]

By the early 1960s, computer control software had evolved from monitor control software, for example IBSYS, to executive control software. Over time, computers got faster while computer time was still neither cheap nor fully utilized; such an environment made multiprogramming possible and necessary. Multiprogramming means that several programs run concurrently. At first, more than one program ran on a single processor, as a result of underlying uniprocessor computer architecture, and they shared scarce and limited hardware resources; consequently, the concurrency was of a serial nature. On later systems with multiple processors, multiple programs may run concurrently in parallel.

Programs consist of sequences of instructions for processors. A single processor can run only one instruction at a time: it is impossible to run more programs at the same time. A program might need some resource, such as an input device, which has a large delay, or a program might start some slow operation, such as sending output to a printer. This would lead to processor being "idle" (unused). To keep the processor busy at all times, the execution of such a program is halted and the operating system switches the processor to run another program. To the user, it will appear that the programs run at the same time (hence the term "parallel").

Shortly thereafter, the notion of a "program" was expanded to the notion of an "executing program and its context". The concept of a process was born, which also became necessary with the invention of re-entrant code. Threads came somewhat later. However, with the advent of concepts such as time-sharing, computer networks, and multiple-CPU shared memory computers, the old "multiprogramming" gave way to true multitasking, multiprocessing and, later, multithreading.

See also

[edit]

Notes

[edit]

References

[edit]

Further reading

[edit]
[edit]
Revisions and contributorsEdit on WikipediaRead on Wikipedia
from Grokipedia
In computing, a process is an instance of a program that is being executed by an operating system, representing an active entity with its own , including , , heap, and stack segments, as well as execution context such as registers and a . This abstraction allows the system to manage multiple programs concurrently, isolating their resources to prevent interference while enabling efficient resource utilization through multitasking. Each process operates independently but can interact with the kernel via system calls for services like or allocation. Central to process management is the Process Control Block (PCB), a kernel that stores vital metadata for each process, including its unique process identifier (PID), current state, parent process ID, CPU registers, memory limits, and accounting information such as used. Processes cycle through distinct states during their lifecycle: new (being initialized), ready (awaiting CPU allocation), running (actively executing), waiting (blocked for an event like I/O completion), and terminated (finished execution). The operating system scheduler selects ready processes for execution, performing context switches—saving the current process's state and loading another's—to achieve concurrency on a single CPU, typically every few milliseconds. The process model emerged in the mid-20th century amid the shift from to multiprogramming systems in the , where multiple jobs were loaded into to overlap CPU with I/O operations, improving on early mainframes. In the , systems enabled interactive multi-user environments, formalizing scheduling and isolation to support concurrent user sessions. Today, processes underpin modern operating systems like Unix derivatives and Windows, facilitating features such as (e.g., and ) and serving as the basis for lightweight threads that share a process's resources for finer-grained parallelism.

Fundamentals

Definition and Characteristics

In computing, a process is defined as a program in execution, serving as the fundamental within an operating system. It represents an active entity that encapsulates the program's code (text segment), data (including global variables), stack (for temporary data and activation records), heap (for dynamic memory allocation), and current execution state, such as the and CPU registers. This structure allows the operating system to manage multiple such instances concurrently, providing each with the illusion of exclusive access to system resources. Key characteristics of a process include its independent , which isolates it from other processes to enhance stability and security by preventing interference with shared resources. This typically divides into user and kernel regions, mapped to physical via mechanisms like page tables. Additionally, a process maintains CPU registers (e.g., for the next instruction, stack pointer, and general-purpose registers), open files, I/O status, and signals for handling events. The operating tracks these attributes in a , a kernel essential for management. A program, in contrast, is a passive, static entity consisting of executable code stored on disk, lacking any runtime or allocated resources. A , however, is a dynamic instantiation of that program, activated upon loading into and assigned execution privileges by the operating system. This distinction underscores the shift from inert instructions to an active computational activity. During execution, the operating system allocates resources to a process on an exclusive basis, including slices of via scheduling, portions of for its , and access to I/O devices and files. These allocations ensure controlled, efficient utilization while maintaining isolation, with resources reclaimed upon process completion to prevent waste.

Process Control Block

The Process Control Block (PCB) is a kernel data structure that encapsulates all information required by the operating system to manage an individual process, serving as the central repository for process-specific details. It includes the process identifier (PID), which uniquely identifies the process within the system; the current process state, indicating whether it is ready, running, or blocked; the , which holds the address of the next instruction to execute; and CPU registers, capturing the processor's state such as general-purpose registers and stack pointers. Additional fields encompass memory-management details like limits on allocation and pointers to page tables for address translation, accounting information such as accumulated and resource usage, and I/O status, including open files and pending operations. These components ensure the kernel can track and control process execution without relying on user-space data. The PCB plays a critical role in operating system operations by enabling efficient context switching and informed scheduling decisions. During context switching, the kernel saves the executing process's CPU context—including registers and the —into its PCB and restores the context from another process's PCB, allowing seamless transitions between processes. For scheduling, the PCB provides essential data like and priority, which the scheduler uses to select the next process for execution from the ready queue. This centralized storage facilitates multitasking by maintaining isolation and restorability of each process's environment. Implementations of the PCB vary across operating systems to suit their architectures and requirements. In Unix-like systems such as , the PCB is realized as the task_struct structure in the kernel, which includes fields for PID (pid), state (state), a memory descriptor (mm) pointing to page tables and areas, CPU registers via the thread_info, metrics like CPU usage (utime and stime), and I/O-related lists for file descriptors. In contrast, Microsoft's Windows uses the EPROCESS structure, featuring UniqueProcessId for PID, process flags for state (e.g., active or terminated), embedded KPROCESS for CPU context including registers, VadRoot for virtual address descriptors and page table management, quota usage for , and counters for I/O operations like read/write transfers. Regarding memory management, the PCB integrates pointers to the process's page tables, which map virtual addresses to physical frames, along with allocation limits to enforce boundaries and prevent overflow. In Linux's task_struct, the mm_struct field links to the page directory base (via pgd), enabling the (MMU) to load the correct translation during context switches. Similarly, Windows' EPROCESS contains offsets to the process environment block (PEB) and directory table base (CR3 register value) for page table access, supporting demand-paging and protection. These elements allow the kernel to handle isolation per process without global interference.

Process Lifecycle

Creation and Initialization

In Unix-like operating systems, new processes are typically created using the fork() system call, which duplicates the calling process to produce an identical child process that shares the parent's code, data, and most resources. The child process then often invokes one of the exec() family of functions, such as execve(), to replace its memory image with a new program loaded from an executable file, while retaining the process identifier and other inherited attributes. The fork-exec model originated from early Unix designs. Modern Unix-like systems enable efficient process creation using copy-on-write techniques, which allow the parent and child to share memory pages until modifications occur. In contrast, Microsoft Windows employs the CreateProcess() API function, which creates a new process and its primary thread in a single call, specifying the executable image, command line, and initial environment without requiring a separate duplication step. Upon creation, the operating system allocates a (PCB) for the new process to store essential metadata, such as the process identifier, register states, and information. The program code and data are then loaded into the process's , with initial stack and heap regions allocated to support execution and dynamic memory needs. The process is initialized in a "ready" state, placed on the scheduler's ready queue, and inherits resources from its parent, including open file descriptors and environment variables that define the execution , such as the system path and current . The creation establishes a parent-child relationship, forming process hierarchies where each child is linked to its creator, often visualized as a tree structure with the init process (PID 1) as the root in Unix-like systems. Incomplete handling of child exit status by the parent can result in zombie processes, which occupy PCB entries until reaped. This inheritance model ensures resource continuity while allowing children to modify their environment independently after setup.

States and Transitions

In operating systems, a process progresses through a series of distinct states during its lifecycle, managed by the kernel to ensure efficient and execution. The standard model, often referred to as the five-state process model, defines these states as New, Ready, Running, Waiting (or Blocked), and Terminated, allowing the operating system to track and transition processes based on system events and resource availability. The New state represents the initial phase where the process is being created, involving allocation of resources like and initialization of the process control block, but it has not yet been admitted to the ready queue. Once creation completes, the process transitions to the Ready state, where it awaits allocation of and is placed in the ready queue, a maintained by the operating to hold processes eligible for execution. In the Running state, the process is actively executing on the CPU, performing instructions until an event interrupts it. The Waiting state occurs when the process is blocked, typically awaiting an I/O operation, timer expiration, or another event, and is moved to an I/O or device queue. Finally, the Terminated state marks the end of execution, where the process has completed normally or been abnormally killed, releasing its resources back to the . State transitions are triggered by specific events and managed through kernel mechanisms, forming a logical flow that can be visualized as a . From New to Ready, admission occurs upon successful initialization; from Ready to Running, the scheduler dispatches the process via a when the CPU becomes available. The Running state can lead back to Ready through preemption (e.g., time slice expiration via ) or to Waiting via a for I/O or blocking operation. Upon event completion, such as I/O finish signaled by a , the process moves from Waiting to Ready. Termination transitions from Running (or sometimes Waiting) to Terminated, often initiated by a like exit or an unhandled . These transitions rely on queues like the ready queue for dispatch candidates and device queues for blocked processes, ensuring orderly progression without overlap. Variations exist across operating systems, such as the seven-state model, which extends the five-state model by adding Suspended Ready and Suspended Blocked states to handle scenarios where processes are temporarily removed from main (swapped out) for resource conservation in memory-constrained environments. In this model, a running process can be suspended to Suspended Ready by the OS or another process, and later resumed to Ready; similarly, a blocked process can enter Suspended Blocked and return to Blocked upon resumption. This extension is common in systems supporting and multitasking under load, though the core five states remain foundational in most modern kernels like those in systems.

Termination and Cleanup

Processes terminate through several distinct mechanisms, each triggering specific operating system actions to ensure orderly conclusion of execution. Normal termination occurs when a process reaches the end of its main function or explicitly calls the exit() system call, prompting the operating system to initiate deletion by releasing resources and updating process tables. Abnormal termination arises from runtime errors, such as division by zero or invalid memory access, or from receiving a termination signal like SIGTERM sent by another process via the kill() system call. Forced termination is imposed by the operating system itself, typically in response to resource exhaustion, security violations, or system-wide shutdown commands, bypassing the process's voluntary exit. This final transition moves the process to a terminated state, where it no longer executes but awaits full reclamation. Following termination, the operating system executes a comprehensive cleanup routine to reclaim resources and maintain system integrity. This includes deallocating the process's , closing all open files and network sockets, releasing any acquired locks or semaphores, and flushing buffered I/O data to disk. The process control block (PCB) is updated to reflect the ended status, and kernel structures associated with the , such as file descriptors and signal handlers, are freed. To notify the , the operating system sets a flag in the child's PCB; the can then invoke the wait() or waitpid() to retrieve termination details and complete the reaping process, preventing indefinite resource holds. A key challenge in termination arises with zombie and orphan processes, which require specific handling to avoid resource leaks. A zombie process forms when a child terminates but its parent has not yet called wait() to reap it; the child releases most resources but retains a minimal entry in the kernel's process table, including its process ID (PID) and exit status, until the parent acknowledges completion. Failure to reap zombies can exhaust the process table, limiting new process creation; if the parent process terminates without reaping its children, the zombies become orphans and are reaped by the init process (or equivalent). Orphan processes occur if the parent terminates before the child; the child is then adopted by the system init process (PID 1), which assumes responsibility for reaping it upon the child's eventual termination, ensuring no processes remain unparented. The provides a mechanism for inter-process signaling during termination, conveying the outcome to the . Passed as an to exit(status), it conventionally uses 0 to indicate successful completion and non-zero values for errors, with specific codes often defined by applications or standards like . The retrieves this status via wait(), shifted left by 8 bits in some implementations to distinguish exit from signal termination, allowing scripts or programs to react accordingly—such as logging failures or chaining dependent tasks. This status is preserved in the zombie's PCB until reaped, ensuring reliable communication even if the delays.

Process Management

Scheduling and Dispatching

In operating systems, scheduling refers to the method by which the kernel selects processes from the ready queue to execute on the CPU, aiming to optimize and system performance. Dispatching then follows as the kernel's mechanism to allocate the CPU to the chosen by saving the state of the currently running and restoring the state of the selected one. This ensures efficient multitasking while balancing competing objectives. The primary goals of CPU scheduling include maximizing CPU utilization, which measures the percentage of time the CPU is busy executing processes rather than idling; maximizing throughput, defined as the number of processes completed per unit time; minimizing , the interval from process submission to completion; minimizing waiting time, the total time a process spends in the ready queue; and minimizing response time, the duration from request submission to the first output for interactive processes. These objectives often against each other, with batch systems prioritizing throughput and , while interactive systems emphasize response time. Scheduling is categorized by timing levels: long-term scheduling decides the degree of multiprogramming by admitting new processes to the ; medium-term scheduling handles swapping processes in and out of memory to manage ; and short-term scheduling, or CPU scheduling, selects the next process to run from the ready queue. Algorithms are further classified as preemptive, where the operating system can a running process to allocate the CPU to another (e.g., due to a timer ), or non-preemptive, where a process runs until it voluntarily yields the CPU, such as upon completion or I/O request. Preemptive scheduling supports better responsiveness in environments but incurs higher overhead from frequent context switches. Common short-term scheduling algorithms include First-Come, First-Served (FCFS), which executes es in the order of their arrival, leading to simple implementation but potential convoy effects where short es wait behind long ones. Shortest Job First (SJF) selects the with the smallest estimated CPU burst time next, minimizing average waiting time for non-preemptive cases and proven optimal under certain assumptions, though it requires accurate burst predictions. Round Robin (RR) allocates a fixed time quantum (typically 10-100 ms) to each in a cyclic manner, using preemption to ensure fairness and low response times in interactive systems, with performance degrading if the quantum is too small due to overhead. Priority scheduling assigns execution order based on priority levels, often incorporating aging to prevent indefinite starvation of low-priority es, but it can lead to if not managed carefully. Dispatching occurs when the scheduler identifies the next process, at which point the kernel dispatcher saves the context (registers, , and process state) of the current process to its and loads the context of the selected process, enabling seamless CPU reassignment. This mechanism is invoked on events like expirations, I/O completions, or process terminations. Key performance metrics for evaluating scheduling effectiveness include , calculated as: Turnaround time=completion timearrival time\text{Turnaround time} = \text{completion time} - \text{arrival time} for each , representing the total time from submission to finish. Waiting time for a is then: Waiting time=turnaround timeburst time\text{Waiting time} = \text{turnaround time} - \text{burst time} where burst time is the actual CPU execution duration, and the average waiting time across nn processes is: Average waiting time=(waiting times)n.\text{Average waiting time} = \frac{\sum (\text{waiting times})}{n}. These metrics quantify fairness and efficiency, with algorithms like SJF often yielding lower averages in simulations compared to FCFS.

Multitasking and Context Switching

Multitasking in operating systems enables the concurrent execution of multiple es, allowing a single processor to handle several tasks by rapidly alternating between them. This concurrency is achieved through two primary models: , where processes voluntarily yield control to the operating after completing a portion of their work, and preemptive multitasking, where the operating forcibly interrupts a running process using hardware timers to allocate to others. Multitasking can further be classified as single-user, supporting multiple processes for one user on a personal like early Windows versions, or multi-user, accommodating simultaneous access by multiple users on shared systems like Unix. Context switching is the core mechanism underlying multitasking, involving the operating system saving the state of the currently executing process—such as register values, , and information—into its (PCB) before loading the state of the next process from its PCB to resume execution. This switch is typically triggered by the scheduler when a process's time slice expires or it blocks for I/O. However, it incurs overhead from flushing processor caches and the (TLB), as these hardware structures hold process-specific data that must be invalidated or reloaded to prevent interference between address spaces, leading to performance penalties from cache misses and TLB refills upon resumption. In systems, context switching creates the illusion of simultaneous execution by allocating short time slices—often milliseconds—to each , enabling multiple users or tasks to appear to run concurrently on a single processor despite sequential actual execution. This approach, foundational to modern interactive , maximizes resource utilization and responsiveness in multi-user environments. Modern enhancements to multitasking leverage hardware advancements for improved efficiency and parallelism. , introduced by , allows a single physical core to appear as multiple logical processors by duplicating architectural state while sharing execution resources, facilitating faster context switches and better utilization during stalls. In multi-core processors, true parallelism emerges as independent cores execute separate processes simultaneously without the need for frequent switching, scaling multitasking beyond the limitations of single-core . The performance impact of context switching includes latency typically ranging from 5 to 20 microseconds on modern hardware, encompassing direct costs like state save/restore and indirect costs from cache and TLB disruptions, which can degrade throughput if switches occur too frequently.

Inter-Process Communication

Communication Mechanisms

In , processes communicate and coordinate through various mechanisms that enable exchange and event notification while maintaining system stability. These methods, primarily defined in standards, allow unrelated processes to interact without , supporting both local and networked environments. Seminal work on in Unix systems emphasized reliable, kernel-mediated channels to facilitate modularity and portability across processes. Shared memory provides a high-performance mechanism where multiple processes the same region of physical into their virtual address spaces, enabling direct . This approach, supported by through functions like shm_open() and mmap(), creates a shared memory object identified by a name, which processes can open and attach independently. Once mapped, processes read and write to the shared region as if it were private memory, but this requires careful to prevent race conditions where concurrent access leads to inconsistent data. Shared memory is particularly efficient for large data transfers in applications like or multimedia processing, as it avoids kernel involvement in each data movement. Message passing offers a structured alternative, where processes exchange fixed or variable-sized messages via kernel-managed buffers, decoupling sender and receiver. POSIX message queues, created and accessed via mq_open(), allow processes to send and receive priority-ordered messages asynchronously, with the queue acting as an intermediary to handle blocking or non-blocking operations. This mechanism supports multiple readers and writers, making it suitable for producer-consumer patterns in distributed systems. Pipes and first-in-first-out (FIFO) devices extend message passing for stream-oriented data: unnamed pipes, created by pipe(), provide unidirectional communication between related processes, such as parent and child after a fork(), with one end for writing and the other for reading. Named pipes, or FIFOs, use mkfifo() to create filesystem-visible entries, enabling unrelated processes to connect via a common path, thus broadening applicability beyond process hierarchies. Sockets, originating from Berkeley Unix, facilitate both local (Unix domain) and network (Internet domain) message passing through a socket API that abstracts transport protocols like TCP or UDP. A server process creates a socket with socket(), binds it to an address and port using bind(), and listens for connections via listen(), while clients initiate communication with connect(). Data is exchanged bidirectionally using send() and recv(), supporting reliable, ordered delivery over networks. This mechanism underpins client-server architectures, such as web services where a browser process connects to a server process across machines. Signals provide an asynchronous, lightweight notification mechanism for event signaling between processes, without data transfer. POSIX defines signals like SIGTERM, sent via kill() to request graceful termination, allowing the receiving process to handle or ignore it for cleanup. Signals are delivered by the kernel interrupting the target process, useful for scenarios like error reporting or job control, but they carry limited information—typically just an integer code—requiring supplementary mechanisms for detailed communication. For instance, Unix shells chain commands using unnamed pipes, as in ls | grep .txt, where the output of ls streams directly into grep for filtering. Similarly, socket-based client-server examples demonstrate a web client sending HTTP requests to a server process, which responds with processed data.

Synchronization Primitives

Synchronization primitives are essential mechanisms in processes to coordinate access to shared resources, preventing race conditions and ensuring consistency in concurrent environments. These tools enable processes to their actions, particularly when multiple processes compete for the same resources, such as or files. By providing structured ways to enforce and manage resource availability, synchronization primitives mitigate issues like inconsistent reads or writes that could arise from uncoordinated access. One fundamental synchronization primitive for achieving mutual exclusion is the semaphore, introduced by Edsger W. Dijkstra in 1965. A semaphore is an integer variable that, apart from initialization, can only be accessed through two atomic operations: wait (also known as P or down) and signal (also known as V or up). The wait operation decrements the semaphore value if it is positive; otherwise, the process blocks until the value becomes positive. The signal operation increments the value and wakes a blocked process if any are waiting. Binary semaphores, initialized to 1, act as locks to ensure only one process enters a at a time, while counting semaphores, initialized to a value greater than 1, manage access to a fixed number of identical resources, such as buffer slots. Semaphores find application in solving classical synchronization problems, such as the producer-consumer problem, where one produces and another consumes it from a shared bounded buffer. In Dijkstra's , three semaphores are used: mutex (binary, for ), empty (counting, for available buffer slots), and full (counting, for filled slots). The waits on empty and mutex before adding and signals full and mutex afterward; the consumer performs symmetric operations with full and empty. This ensures the buffer neither overflows nor underflows while maintaining exclusive access during updates. Another classical problem is the dining philosophers, posed by Dijkstra in 1965 to illustrate and deadlock risks. Five philosophers sit around a table, each needing two adjacent forks to eat; forks represent shared resources. A naive solution where each philosopher picks up the left fork then the right can lead to deadlock if all pick left simultaneously. Semaphores can resolve this by modeling forks as binary semaphores, with processes waiting and signaling appropriately, though care is needed to avoid circular waits. The readers-writers problem, first formulated by Courtois, Heymans, and Parnas in 1971, addresses concurrent access to a shared database where multiple readers can access simultaneously but writers require exclusive access. Solutions using s distinguish reader and writer counts: a mutex semaphore protects the reader count, and a resource semaphore (binary for writer priority or counting for reader preference) controls access. In the first variant favoring readers, writers wait if any reader is active; in the second, writers gain priority to prevent . Monitors represent a higher-level synchronization construct, proposed by C.A.R. Hoare in as a structured alternative to . A monitor is a module encapsulating shared data and procedures, with implicit : only one process executes monitor code at a time. For coordination beyond exclusion, monitors include condition variables supporting wait (blocks the process and releases the monitor) and signal (wakes a waiting process, which then re-enters the monitor). This design reduces errors from manual semaphore management by confining within the monitor. At the hardware level, atomic operations provide low-level support for primitives. The instruction atomically reads a location and sets it to 1, returning the original value; if 0, the process acquires the lock, enabling implementations for without OS intervention. This primitive, available in many processor architectures since the 1960s, underpins software locks by allowing busy-waiting until the lock is free. Similarly, (CAS) atomically compares a value to an and, if equal, replaces it with a new value, returning success or failure. CAS supports lock-free algorithms by enabling optimistic updates, retrying only on contention, and is a cornerstone of non-blocking in modern multiprocessors. Deadlocks, where processes indefinitely wait for resources held by each other, can arise from improper use of synchronization primitives. Prevention strategies include resource ordering, as outlined by Coffman, Elphick, and Shoshani in 1971: assign unique numbers to resource types and require processes to request resources in strictly increasing order, eliminating circular waits. Another approach is the , developed by Dijkstra in 1965, which avoids deadlocks by simulating resource allocations to ensure a safe sequence exists where all processes can complete without deadlock, treating the system like a banker granting loans only if repayment is feasible.

Advanced Concepts

Processes vs Threads

In computing, a thread is defined as a basic unit of execution within a process, consisting of a , a stack, and a set of registers, while sharing the process's , code, data, and other resources such as open files. Unlike , which maintain isolated virtual s to ensure independence and , threads within the same process communicate efficiently through but lack the same level of isolation, increasing the risk of one thread corrupting others via race conditions or invalid memory access. This shared structure results in significantly lower overhead for thread creation and termination compared to —often by orders of magnitude—since no new allocation is required, and switching between threads is faster, involving only the preservation of thread-specific state rather than the full process . Threading models vary in implementation to balance performance and . User-level threads are managed entirely by a thread in user space, without kernel awareness, enabling rapid scheduling and creation but limiting true parallelism on multiprocessor systems since the kernel treats the entire as a single scheduling unit. In contrast, kernel-level threads are recognized and scheduled directly by the operating system kernel, supporting efficient distribution across multiple CPUs but incurring higher overhead due to system calls for each operation. Many modern systems employ hybrid models, such as many-to-one or many-to-many mappings, where user-level threads are multiplexed onto fewer kernel threads to combine the benefits of both approaches. A prominent example is the Threads (pthreads) , a standard defined in .1c for creating and managing threads in C programs on systems, providing functions like pthread_create for spawning threads and pthread_join for . Processes are typically preferred for scenarios requiring strong isolation, such as web servers like in prefork mode, where each client request spawns a separate to handle potential faults without affecting the parent server. Threads, however, excel in applications demanding high concurrency within a single , such as video games where separate threads manage parallel tasks like graphics rendering, physics simulation, and input handling to improve on multicore processors. Context switching between threads remains lighter than for processes, facilitating smoother multitasking in these resource-intensive environments. The (JVM) illustrates a hybrid threading approach, where Java threads—abstracted via the java.lang.Thread class—are implemented by mapping them to native operating system threads, often using a combination of user-level management in the JVM and kernel-level scheduling for true parallelism across cores. This model allows Java applications to leverage platform-specific threading efficiencies while maintaining portability, as seen in server-side applications like Tomcat, which use threads for concurrent request processing within a single JVM process.

Security and Isolation

One of the fundamental aspects of process security in operating systems is address space isolation, achieved through virtual memory mechanisms that assign each process a private virtual address space. This prevents one process from directly accessing or modifying the memory of another, thereby protecting against unauthorized data corruption or leakage. For instance, the operating system translates virtual addresses used by a process into physical addresses, ensuring that memory accesses are confined to the allocated region, with hardware support like page tables enforcing these boundaries. To further enforce security, operating systems implement privilege levels, distinguishing between user mode and kernel mode. In user mode, operate with restricted privileges, unable to execute sensitive instructions or access hardware directly, while kernel mode grants full access for system operations. Controlled transitions between these modes occur via system calls, where a user-mode process invokes a kernel service through a protected interface, such as a trap instruction, allowing the kernel to validate and execute the request without compromising isolation. Sandboxing extends these protections by restricting process actions beyond basic memory and privilege controls, using mechanisms like Linux capabilities, SELinux policies, and . Linux capabilities decompose privileges into granular subsets (e.g., CAP_SYS_ADMIN for administrative tasks), enabling processes to perform specific operations without full access, thus limiting potential damage from compromised code. SELinux, a (MAC) framework, enforces fine-grained policies based on security contexts for subjects (processes) and objects (files, ports), confining processes to predefined domains that restrict file reads/writes, network access, and transitions. Similarly, uses path-based profiles to confine applications to allowed resources, such as specific directories or network interfaces, providing an easier-to-configure MAC layer that complements discretionary access controls. Modern threats to include buffer overflows, which can overwrite adjacent memory and enable , and side-channel attacks, such as timing-based leaks that infer address layouts. Mitigations like (ASLR) randomize the base addresses of key memory regions (stack, heap, libraries) at load time, complicating exploitation by making return addresses and pointers unpredictable. These defenses, often combined with non-executable memory (e.g., DEP/W^X), significantly raise the bar for attackers targeting process boundaries. Containers provide lightweight isolation for processes by leveraging kernel features like namespaces, allowing multiple processes to share the host kernel while appearing isolated in their own views of resources (e.g., PID, network, mount namespaces). In Docker, for example, user namespaces remap container UIDs to non-privileged host UIDs, preventing escaped processes from gaining access on the host, thus enhancing security without full virtualization overhead.

Historical Development

Early Concepts

In the pre-operating system era of the 1950s, computing relied on for mainframes like the , where jobs were submitted sequentially via punched cards or tape, executed one at a time without overlap or true concurrency, as the system lacked mechanisms for managing multiple executing entities. This approach, implemented by organizations such as Research Laboratories for the starting in the early 1950s, prioritized efficient use of expensive hardware through offline preparation of job decks but resulted in long turnaround times and idle CPU periods during I/O operations. The introduction of multiprogramming in the early 1960s marked a pivotal shift, enabling multiple programs to reside in memory simultaneously and share resources to overlap computation and I/O. The Atlas computer, developed at the and operational from 1962, pioneered this by supporting up to 16 concurrent jobs through its supervisor, which managed a one-level storage system combining core memory and drums for virtual addressing. This allowed efficient memory sharing via fixed-size 512-word pages and page-address registers, reducing idle time and improving throughput on what was then the world's fastest computer. Led by Tom Kilburn, the Atlas design emphasized automatic without user intervention, laying groundwork for modern process abstraction. Edsger W. Dijkstra's , implemented in 1965–1968 at , provided the first formal framework for process management by structuring the OS as a hierarchy of sequential es synchronized via semaphores. Each , representing activities like user programs or I/O handlers, operated independently with explicit through P (wait) and V (signal) operations, ensuring deadlock-free coordination in a layered design verified for logical soundness. This cooperative multiprogramming model on an Electrologica X8 computer prioritized smooth user program flow, influencing subsequent OS structures by formalizing es as independent, synchronizable entities. Time-sharing systems in the mid-1960s further evolved concepts by enabling interactive multi-user access, with the (CTSS) at MIT, demonstrated in 1961 on an IBM 7094, introducing process swapping and priority scheduling for up to 30 users. CTSS's supervisor managed user in a separate memory bank, emulating I/O and providing per-user file systems, which solidified the "" as a protected . Building on this, (Multiplexed Information and Computing Service), initiated in 1965 by MIT, , and , extended with segmented and on GE-645 hardware, supporting hierarchical as a precursor to Unix's process model. Under Fernando J. Corbató's leadership, emphasized secure, multiplexed access, influencing Unix developers who adapted its process and file concepts after leaving the project in 1969.

Evolution in Modern Systems

The fork-exec model, introduced by Ken Thompson and Dennis Ritchie in early Unix (First Edition, 1971), became a foundational influence on process creation in modern operating systems by allowing a parent process to spawn a child via the fork() system call, which duplicates the process, followed by exec() to replace the child's image with a new program. This lightweight approach to process spawning emphasized efficiency and portability, shaping Unix-like systems' process management for decades. In the 1990s, Microsoft Windows NT introduced an object-based model for processes, where processes and resources are represented as kernel objects accessed via handles, enabling secure and modular across subsystems. This design facilitated multitasking in enterprise environments by abstracting processes as protected entities within the NT executive, contrasting with Unix's more procedural style. The open-source , building on Unix heritage, saw significant process enhancements in its 2.6 series during the 2000s, including improved real-time scheduling policies like SCHED_FIFO and SCHED_RR, which prioritized low-latency task execution for embedded and applications. These updates, integrated into the (CFS), enhanced preemptibility and reduced context-switch overhead, making viable for real-time systems without specialized patches. Virtualization technologies in the 2000s, exemplified by VMware's ESX introduced in 2001, treated guest operating system processes as isolated execution units within , allowing multiple OS instances to share host hardware while maintaining process boundaries through hardware-assisted . This abstraction enabled efficient resource allocation for processes across virtualized environments, paving the way for consolidation. From the 2010s onward, cloud-native paradigms redefined processes through and ; , launched in 2014, organizes processes into pods—atomic groups of one or more containers sharing network and storage namespaces, treated as the smallest deployable units for scalable application management. Complementing this, models like , available since 2014, execute functions as short-lived, ephemeral processes that scale automatically without persistent state, using configurable temporary storage up to 10 GB for data-intensive tasks. By 2025, recent advancements include (extended ) programs for dynamic process monitoring at the kernel level, enabling real-time tracing of process events like creation, execution, and resource usage without modifying kernel code, as seen in tools for in cloud environments. Similarly, (Wasm) has emerged as a standard for sandboxed processes in browsers, compiling code to a secure, portable binary format that runs in isolated virtual machines, enhancing performance and security for untrusted code execution. These trends reflect a shift toward more distributed, secure, and efficient process models in landscapes.

References

Add your contribution
Related Hubs
Contribute something
User Avatar
No comments yet.