Process Concept

A deep dive into process states, Process Control Block (PCB) architecture, and the mechanics of process creation in modern operating systems.

published: reading time: 24 min read author: GeekWorkBench

Process Concept

Every program you run, every daemon that keeps your server alive, every background service humming along in the cloud — all of them are processes. Understanding processes is the foundation of operating systems knowledge. Without this bedrock, you’ll find yourself lost when debugging that mysterious CPU spike at 3 AM or trying to explain why your application is not utilizing all those cores you paid for.

A process is essentially a program in execution. But here’s where it gets interesting: a program is passive (just bytes on disk), while a process is active — it has state, context, and a life cycle. This distinction matters more than you might think.

Introduction

The operating system must manage processes efficiently. It needs to create them, schedule them, allow them to communicate, and eventually terminate them. To do this, the OS maintains a data structure for each process — the Process Control Block (PCB) — which acts as the process’s fingerprint in the system.

When you execute a command in your terminal, the shell creates a new process by forking itself. The child process then typically calls exec to replace its memory image with the program you wanted to run. This pattern of fork-exec is fundamental to Unix-like systems and understanding it will save you countless hours of debugging.

When to Use

  • Debugging performance issues — When CPU usage is unexpectedly high or low, understanding process states helps identify bottlenecks.
  • Designing concurrent systems — Knowing how processes are scheduled allows you to write more efficient parallel code.
  • System programming — Creating daemons, forking workers, or managing subprocess hierarchies requires solid process concepts.
  • Capacity planning — Understanding how many processes your system can handle informs infrastructure decisions.

When Not to Use

  • Writing simple scripts — For basic automation, you rarely need to think about process internals.
  • High-level application development — Modern runtimes (JVM, Node.js, .NET) abstract away process management for most use cases.
  • Database query optimization — Process concepts won’t help you tune your SQL indexes.

Process States

A process doesn’t simply exist as “running” or “not running.” The operating system defines several distinct states that a process can occupy:

graph TD
    A[New] --> B[Ready]
    B --> C[Running]
    C --> D[Waiting]
    D --> B
    C --> E[Terminated]
    B --> E

New: The process is being created. The OS is allocating memory, initializing the PCB, and setting up the address space.

Ready: The process is in memory and waiting to be assigned to a CPU core. The scheduler will pick it when a core becomes available.

Running: The process is actively executing instructions on a CPU core. Only one process (per core) can be in this state at any given moment.

Waiting (Blocked): The process cannot continue execution because it’s waiting for some event — I/O completion, a signal, a resource becoming available, or inter-process communication.

Terminated: The process has finished execution. The OS cleans up resources but may retain the PCB temporarily for the parent to retrieve exit status.

Process Control Block (PCB)

The PCB is the kernel’s representation of a process. It’s a structure — usually defined in the OS source code — that contains all the information the kernel needs to manage a process.

The PCB lives in kernel memory and is never directly accessible to user programs. However, on Linux, you can inspect much of this information via the /proc filesystem — each process has a directory at /proc/<pid>/.

Key PCB fields include:

  • Process ID (PID): Unique identifier
  • Parent PID (PPID): Who created this process
  • State: Current execution state
  • Program Counter: Next instruction to execute
  • CPU Registers: Current register values
  • Stack Pointer: Current top of stack
  • Memory Management Info: Page tables, segments
  • I/O Status: Open files, pending I/O operations

Process Creation

In Unix-like systems, processes are created via the fork-exec pattern:

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main() {
    pid_t pid = fork();

    if (pid < 0) {
        perror("fork failed");
        return 1;
    }

    if (pid == 0) {
        // Child process
        printf("Child: PID = %d, Parent PID = %d\n", getpid(), getppid());

        // Replace with new program
        char *args[] = { "ls", "-la", NULL };
        execvp("ls", args);

        // If execvp fails
        perror("exec failed");
        return 1;
    } else {
        // Parent process
        printf("Parent: PID = %d, Child PID = %d\n", getpid(), pid);

        int status;
        waitpid(pid, &status, 0);  // Wait for child
        printf("Child exited with status: %d\n", WEXITSTATUS(status));
    }

    return 0;
}

The fork() system call creates a new process by duplicating the current process. After fork, both the parent and child continue execution from the same point — the only difference is that fork returns the child’s PID in the parent and 0 in the child.

Architecture Diagram

graph TB
    subgraph Kernel_Space
        PCB1[PCB: PID 1001]
        PCB2[PCB: PID 1002]
        PCB3[PCB: PID 1003]
        Scheduler[Scheduler]
    end

    subgraph User_Space
        Process1[Process 1001]
        Process2[Process 1002]
        Process3[Process 1003]
    end

    Process1 --> PCB1
    Process2 --> PCB2
    Process3 --> PCB3

    Scheduler -->|schedules| PCB1
    Scheduler -->|schedules| PCB2
    Scheduler -->|schedules| PCB3

    ReadyQ[Ready Queue] --> Scheduler
    WaitQ[Waiting Queue] --> Scheduler

The scheduler maintains multiple queues: ready processes wait in the ready queue, while blocked processes wait in the waiting queue. The scheduler picks from the ready queue based on the scheduling algorithm in use.


Core Concepts

Process vs Thread

A key distinction: processes have separate address spaces, while threads share the same address space within a process. Creating a thread is cheaper than creating a process because no memory duplication is needed.

Parent-Child Hierarchy

Processes form a tree. Every process (except init) has a parent. If a parent dies before its child, the child is “adopted” by init (PID 1). You can see this hierarchy with pstree on Linux.

Zombie and Orphan Processes

A zombie is a process that has terminated but whose parent hasn’t yet called wait() to retrieve the exit status. Zombies remain in the process table until the parent reads their status.

An orphan is a process whose parent has died. The init process adopts orphans and eventually reaps them via wait.

Daemon Processes

A daemon is a background process that runs without a controlling terminal. Created by forking, then calling setsid() to detach from the terminal, then often changing the working directory to / and closing stdin/stdout/stderr.


Production Failure Scenarios

Fork Bombs

Problem: A process rapidly forks children that also fork, exhausting process table slots and PID resources.

Symptoms: “fork: Cannot allocate memory” errors, system unresponsiveness, inability to create new processes even for essential services.

Mitigation:

  • Set appropriate ulimit -u (max processes per user)
  • Use systemd’s DefaultLimitNPROC in /etc/security/limits.conf
  • Implement exponential backoff in application code that forks
# Check current process limits
ulimit -a
# See process count per user
ps aux | awk '{print $1}' | sort | uniq -c | sort -rn | head

Zombie Accumulation

Problem: Application fails to call wait() on terminated children, causing zombie processes to accumulate.

Symptoms: ps shows processes with ‘Z’ state, process table fills up, new processes cannot be created.

Mitigation:

  • Always call wait()/waitpid() in parent
  • Use signal handler for SIGCHLD that reaps children
  • For long-running servers, implement a child reaper thread
#include <signal.h>

void sigchld_handler(int sig) {
    int saved_errno = errno;  // Save errno
    while (waitpid(-1, NULL, WNOHANG) > 0);
    errno = saved_errno;
}

struct sigaction sa;
sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
sigaction(SIGCHLD, &sa, NULL);

Resource Leakage in Child Processes

Problem: Child processes inherit open file descriptors but never close them, leading to resource exhaustion.

Symptoms: “Too many open files” errors, file descriptor leaks visible in /proc/<pid>/fd/.

Mitigation:

  • Set O_CLOEXEC flag when opening files
  • Close unnecessary FDs before exec()
  • Use FD_CLOEXEC with fcntl() on inherited FDs

Trade-off Table

AspectProcessThread
Creation SpeedSlower (memory duplication)Faster (shared address space)
Memory OverheadHigher (separate page tables)Lower (shares parent’s memory)
CommunicationComplex (IPC required)Simple (shared memory)
Fault IsolationStrong (separate address space)Weak (crash can affect others)
SynchronizationSimpler (no shared state)Complex (must synchronize shared data)
ScenarioBest ChoiceReason
Isolated executionProcessCrash doesn’t affect parent
High-frequency spawningThreadLower overhead
CPU-bound parallel workThreadShares memory, low latency
I/O-bound concurrent tasksEitherDepends on isolation needs
Scheduler DesignAdvantagesDisadvantages
Short-term (CPU)Minimizes turnaround timeMay starve processes
Long-term (Job)Controls degree of multiprogrammingSlower response to load changes
Medium-termBalances memory and CPU usageAdds complexity

Implementation Snippets

Creating a Daemon

#!/usr/bin/env python3
import os
import sys

def become_daemon():
    """Fork and detach from controlling terminal."""
    # First fork
    if os.fork() > 0:
        sys.exit(0)  # Parent exits

    # Detach from controlling terminal
    os.setsid()

    # Second fork (prevents acquiring new controlling terminal)
    if os.fork() > 0:
        sys.exit(0)

    # Change to root directory (prevents holding mount point)
    os.chdir("/")

    # Close stdin, stdout, stderr
    sys.stdout.flush()
    sys.stderr.flush()
    with open(os.devnull, 'r') as devnull:
        os.dup2(devnull.fileno(), sys.stdin.fileno())
    with open(os.devnull, 'a+') as devnull:
        os.dup2(devnull.fileno(), sys.stdout.fileno())
        os.dup2(devnull.fileno(), sys.stderr.fileno())

    # Write PID file
    with open('/var/run/mydaemon.pid', 'w') as f:
        f.write(str(os.getpid()))

become_daemon()
# Now running as daemon
import time
while True:
    time.sleep(60)

Checking Process State on Linux

#!/bin/bash
# Monitor process state transitions

PID=$1
if [ -z "$PID" ]; then
    echo "Usage: $0 <pid>"
    exit 1
fi

echo "Monitoring process $PID..."
echo "Press Ctrl+C to stop"

while true; do
    if [ -d "/proc/$PID" ]; then
        STATE=$(cat /proc/$PID/status | grep "^State:" | awk '{print $2}')
        CMD=$(cat /proc/$PID/cmdline 2>/dev/null | tr '\0' ' ' | cut -c1-60)
        THREADS=$(cat /proc/$PID/status | grep "^Threads:" | awk '{print $2}')
        echo "$(date '+%H:%M:%S') State=$STATE Threads=$THREADS CMD=$CMD"
    else
        echo "Process $PID no longer exists"
        break
    fi
    sleep 1
done

Process Resource Monitoring in C

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>

void print_rlimits(const char *name) {
    struct rlimit limit;
    printf("%s limits for PID %d:\n", name, getpid());

    if (getrlimit(RLIMIT_CPU, &limit) == 0) {
        printf("  CPU time: %lu (soft) / %lu (hard)\n",
               (unsigned long)limit.rlim_cur,
               (unsigned long)limit.rlim_max);
    }
    if (getrlimit(RLIMIT_NPROC, &limit) == 0) {
        printf("  Max processes: %lu (soft) / %lu (hard)\n",
               (unsigned long)limit.rlim_cur,
               (unsigned long)limit.rlim_max);
    }
    if (getrlimit(RLIMIT_NOFILE, &limit) == 0) {
        printf("  Open files: %lu (soft) / %lu (hard)\n",
               (unsigned long)limit.rlim_cur,
               (unsigned long)limit.rlim_max);
    }
}

int main() {
    print_rlimits("Current process");
    return 0;
}

Observability Checklist

Metrics to Monitor

  • Process count per user: ps aux | awk '{print $1}' | sort | uniq -c | sort -rn | head
  • Zombie count: ps aux | grep ' Z ' | wc -l
  • Runnable processes: vmstat 1 | tail -1 (look at ‘r’ column)
  • Process state distribution: ps -eo state,pid,cmd | sort | uniq -c | sort -rn

Logs to Watch

  • /var/log/syslog or /var/log/messages — system process creation/termination
  • dmesg | grep -i "process" — kernel messages about process events
  • journalctl -u <service> — for systemd-managed services

Alerts to Configure

  • Zombie process count > 0 for more than 5 minutes
  • Total process count approaching kernel.pid_max (default 32768 on Linux)
  • Process count per user exceeding 80% of their ulimit
  • High rate of fork() failures in application logs

Trace Commands

# Trace process creation
sudo strace -f -e trace=fork,vfork,clone,execve

# Trace signal delivery
sudo strace -p <pid> -e trace=signal

# Monitor process state changes with perf
sudo perf sched record -a -g sleep 10
sudo perf sched latency

Common Pitfalls / Anti-Patterns

Process Isolation

Modern OSes use hardware virtualization (Intel VT-x, AMD-V) and address space layout randomization (ASLR) to isolate processes. Ensure these are enabled in your BIOS and kernel configuration.

Privilege Separation

Run services with minimal privileges. Use setuid and setgid binaries cautiously — these are common privilege escalation vectors.

# Check for setuid binaries (audit regularly)
find / -perm -4000 -type f 2>/dev/null | head -20

Resource Limits

Configure /etc/security/limits.conf to prevent fork bombs and resource exhaustion:

* soft nproc 1024
* hard nproc 4096
* soft nofile 1024
* hard nofile 4096

Capability Model

Linux capabilities split root privileges into fine-grained units. Prefer dropping capabilities with prctl(PR_CAPBSET_DROP, ...) rather than running as root.

Audit Requirements

For compliance (PCI-DSS, SOC2), log process creation and termination:

# Enable process accounting (Debian/Ubuntu)
sudo apt install acct
sudo systemctl enable acct

Common Pitfalls / Anti-patterns

Ignoring SIGCHLD

Not handling SIGCHLD causes zombies. Always either:

  • Block SIGCHLD and call wait() in a dedicated thread
  • Set SA_NOCLDSTOP and reap in a signal handler
  • Ignore SIGCHLD entirely (only if you never need to wait)

Fork without exec

Calling fork() and then continuing in the child without exec() is often wasteful. If you don’t need the parent’s memory image, consider vfork() (deprecated) or clone() with appropriate flags.

Not checking fork() return value

Always check if fork() failed (returns -1). Fork can fail due to process table exhaustion.

Zombie accumulation in production

Applications that fork workers must implement proper reaping. Use waitpid(-1, NULL, WNOHANG) in a loop to reap all terminated children.

Unintended forking loops

Ensure your code cannot enter a path where fork() is called in a loop without proper exit conditions.


Quick Recap Checklist

  • A process is a program in execution with its own address space and state
  • Process Control Block (PCB) stores all metadata about a process
  • Processes transition through states: New → Ready → Running → Waiting → Terminated
  • fork() creates a new process; exec() replaces the program’s memory
  • Copy-on-write makes fork() efficient even with large memory footprints
  • Zombies form when parent doesn’t call wait(); orphans are adopted by init
  • Daemons are background processes detached from controlling terminals
  • ulimits prevent resource exhaustion; monitor process counts in production
  • Always handle SIGCHLD to prevent zombie accumulation

Interview Questions

1. What is the difference between a process and a program?

A program is a passive entity — a file containing executable code and data stored on disk. A process is an active entity — a program that has been loaded into memory and is currently executing with its own execution context, including registers, stack, heap, and program counter.

In technical terms, a process is the unit of execution in an operating system, while a program is the static description of that execution.

2. Explain the different process states and transitions between them.

A process can be in one of five primary states:

  • New: The OS is creating the process, allocating the PCB and initializing address space.
  • Ready: The process is in memory and waiting to be scheduled on a CPU core.
  • Running: Instructions are being executed on a CPU core.
  • Waiting: The process cannot continue because it's blocked waiting for I/O, a signal, or resource.
  • Terminated: Execution has completed, but the PCB remains until the parent retrieves exit status.

Transitions: New→Ready (setup complete), Ready→Running (scheduler picks it), Running→Waiting (I/O or blocking event), Waiting→Ready (event completes), Running→Ready (time slice expired or preemption), Running→Terminated (exit syscall), Ready→Terminated (parent reaps before scheduling).

3. What is a zombie process and how does it form?

A zombie is a process that has terminated but whose entry still exists in the process table because the parent hasn't read its exit status via wait() or waitpid().

When a process terminates, the kernel retains its PCB temporarily so the parent can retrieve the exit code and resource usage statistics. If the parent never calls wait(), this data is never read and the process entry persists as a zombie. Zombies cannot be killed (they're already dead) and must be reaped by their parent.

To eliminate zombies, either fix the parent process to properly reap children, or kill the parent (then init adopts and reaps them).

4. How does copy-on-write (COW) optimize the fork() system call?

Without COW, fork() would need to copy the entire parent's memory (stack, heap, code) to the child's address space before allowing either to execute. This is expensive for processes with gigabytes of mapped memory.

With COW, fork() creates a new PCB and page tables but doesn't immediately copy the physical memory pages. Instead, both parent and child share the same physical pages, marked as read-only. As soon as either process tries to modify a page, the CPU raises a page fault. The kernel then creates a private copy of that page for the writing process, and the other process keeps the original.

This optimization makes fork() nearly instantaneous regardless of the process's memory footprint, while still maintaining the separate address spaces that processes require.

5. What is the purpose of the Process Control Block (PCB) and what information does it contain?

The PCB is the kernel's data structure that represents a process. It contains all information needed to manage, schedule, and control the process:

  • Identification: PID, parent PID, user ID, group ID
  • State: Current process state (running, waiting, etc.)
  • Scheduling: Priority, cumulative CPU time, scheduling policy
  • Memory: Pointer to memory descriptor (page tables, segments)
  • CPU context: Register values, program counter, stack pointer
  • I/O status: Open files, file descriptor table, current directory
  • Accounting: Start time, total CPU time used
  • Signals: Pending signals, signal handler table

When a process is context-switched out, its CPU context is saved to the PCB. When it's rescheduled, the PCB contents are restored to the CPU.

6. What is the difference between an orphan process and a zombie process?

An orphan is a process whose parent has terminated. The kernel re-parents such processes to init (PID 1), which periodically calls wait() to reap them. Orphans are not problematic — they are cleaned up automatically.

A zombie is a process that has terminated but whose PCB entry remains in the process table because the parent hasn't called wait() to retrieve the exit status. Unlike orphans, zombies cannot be killed (they are already dead) and will persist until the parent reaps them.

The key difference: orphans are still running (from the kernel's perspective) until they terminate, while zombies are already terminated but not yet cleaned up.

7. How does a daemon process differ from a regular background process?

A daemon is a background process that has detached from its controlling terminal — it has no terminal associated with it, which means it won't receive terminal-generated signals like SIGINT (Ctrl+C) or SIGHUP (terminal hangup).

Creation steps: fork() → setsid() (detach from terminal) → optionally fork again (prevent acquiring a new terminal) → chdir("/") (prevent holding a mount point) → close stdin/stdout/stderr → write PID file.

Regular background processes started with & are still attached to the terminal and will receive SIGHUP when the terminal closes. Daemons are immune to terminal closure.

8. What happens during a context switch at the hardware level?

A context switch involves several hardware-level operations:

  • Timer interrupt fires, transferring control to kernel mode
  • Kernel saves current process's registers, program counter, and stack pointer onto the kernel stack
  • Kernel switches to the process's kernel stack (for that process's kernel-mode execution)
  • Scheduler selects a new process and loads its saved registers from the PCB
  • Stack pointer is switched to the new process's kernel stack
  • Control returns to user mode with the new process's instruction pointer

On x86-64, this involves saving/restoring 16 general-purpose registers, RIP, RSP, RFLAGS, segment registers, and FPU/SSE state if used.

9. What is the role of the init process (PID 1) in Unix systems?

The init process is the first user-space process, started by the kernel at boot. It serves as the ancestor of all other processes — every process is either a direct or indirect child of init.

Key responsibilities:

  • Zombie reaping: init calls wait() on all orphaned children, preventing zombie accumulation
  • System initialization: runs startup scripts (/etc/rc.d/, systemd units) to bring up services
  • Adoption: re-parents any process whose parent terminates, ensuring no process is orphaned

On modern systems, systemd or openrc often replaces init's traditional role, but the reaping responsibility remains critical.

10. What is the relationship between PID, TGID, and PGID?

PID (Process ID) is the unique identifier for a process. TGID (Thread Group ID) is the PID of the thread that started a thread group — all threads in a multi-threaded process share the same TGID. PGID (Process Group ID) groups related processes for signal delivery (e.g., a pipeline of processes).

For a single-threaded process, PID == TGID. For the main thread of a multi-threaded process, PID == TGID. Additional threads get unique PIDs but share the TGID.

11. What is the purpose of the nice value and how does it affect scheduling?

The nice value (-20 to +19, default 0) adjusts a process's scheduling priority on Unix systems. Higher nice values mean lower priority — the process is "nicer" to other processes by yielding CPU more readily.

Negative nice values (requires root) increase priority above normal. Positive nice values decrease priority. The kernel converts nice to a static priority value that influences CPU time distribution.

On Linux, CFS ignores nice values for normal scheduling (uses vruntime), but nice values affect the latency target. Batch and idle-class schedulers use nice directly for priority.

12. How does the kernel handle process limits (ulimit)?

The kernel enforces resource limits via the rlimit mechanism. Each process has a soft limit (what the process can change) and a hard limit (root-only ceiling). Limits include max processes (RLIMIT_NPROC), max open files (RLIMIT_NOFILE), max file size (RLIMIT_FSIZE), and CPU time (RLIMIT_CPU).

When a process hits a limit, the kernel returns an error — fork() returns EAGAIN, open() returns EMFILE, etc.

13. What is the difference between vfork() and fork()?

vfork() was introduced as an optimization before copy-on-write existed. It creates a child process without copying the parent's page tables — the child runs in the parent's address space until it calls exec() or exit(). The parent is blocked while the child runs.

vfork() is essentially obsolete now because COW makes fork() efficient. On modern Linux, vfork() is implemented as fork() with COW disabled for comparison purposes.

fork() with COW is faster in practice because many processes only read memory, requiring zero actual copying.

14. What happens when a process calls the exit() system call?

exit() performs several cleanup operations:

  • Closes all open file descriptors
  • Releases memory (page tables, file mappings)
  • Notifies the parent via SIGCHLD (if not ignored)
  • Changes state to ZOMBIE, retaining the PCB for parent to read
  • Stores exit code (passed to exit() or returned from main)
15. How does process forking interact with threads in a multi-threaded program?

Forking a multi-threaded program creates a child with only the calling thread — all other threads disappear. This is a critical and often misunderstood behavior.

Only the thread that called fork() continues in the child. Other threads are not replicated. This leads to subtle bugs: a thread holding a mutex during fork() can leave that mutex locked permanently in the child.

Best practice: fork() followed immediately by exec() in the child, so the parent's address space is replaced entirely.

16. What is the relationship between the kernel's process table and the /proc filesystem?

The kernel maintains a process table as an array of PCB structures (task_struct in Linux). Each entry is identified by a PID and contains all process metadata.

The /proc filesystem is a pseudo-filesystem that exposes kernel data structures as files. Each running process has a directory /proc// containing:

  • /proc//status — process state and resource usage
  • /proc//maps — memory mappings
  • /proc//fd — open file descriptors
17. What is a process group and when is it useful?

A process group is a collection of processes that share a PGID (process group ID). The group leader has a PGID equal to its PID. All members of a group receive signals together — pressing Ctrl+C sends SIGINT to all processes in the foreground process group.

Shells use process groups to manage pipelines: grep foo file | sort creates a process group containing grep and sort.

18. How does the kernel track CPU time per process?

The kernel tracks CPU time in the PCB via utime (user-mode CPU time) and stime (kernel-mode CPU time), measured in clock ticks. These accumulate during each context switch.

On Linux, /proc//stat shows these values (field 14-15). The times command reads them to display total CPU usage.

Accounting happens at every tick (typically 100Hz or 1000Hz depending on CONFIG_HZ), which is why high tick rates increase scheduling overhead.

19. What is the difference between wait(), waitpid(), and wait3()/wait4()?

wait() suspends the caller until any child terminates, returning its PID and exit status. It blocks if no child has exited yet.

waitpid(pid, status, options) waits for a specific child (pid=-1 for any child). WNOHANG option makes it non-blocking — returns 0 if no child has exited.

wait3() and wait4() are older Unix variants that additionally fill a rusage struct with the child's resource usage. WIFEXITED, WEXITSTATUS, WIFSIGNALED, WTERMSIG macros extract information from the status integer.

20. How does containerization (Docker) relate to process concepts?

Docker containers are processes with isolated namespaces. Each container sees its own PID 1, its own network stack, its own filesystem mount, and its own process table — all created by namespace syscalls (clone with CLONE_NEW* flags).

Key namespaces: PID (isolated process numbering), NET (separate network stack), NS (mount points), USER (separate UID/GID mapping), IPC (separate SysV IPC objects).

cgroups (control groups) limit CPU, memory, and I/O per container. Namespaces + cgroups + capabilities = containers.

Further Reading


Conclusion

Processes are the fundamental unit of execution in any operating system. Understanding their lifecycle—creation, scheduling, communication, and termination—provides the foundation for debugging performance issues, designing concurrent systems, and writing robust system software.

The concepts covered here—PCB architecture, fork-exec patterns, zombie and orphan processes, and daemon creation—apply across all Unix-like systems and inform how modern runtimes and container systems work. For example, when Docker runs a container, it’s essentially creating a process with specific namespace isolations.

Continue your learning by exploring process scheduling algorithms, inter-process communication mechanisms (pipes, message queues, shared memory), and thread implementation. These topics build directly on the process concepts you’ve mastered here.

Category

Related Posts

ASLR & Stack Protection

Address Space Layout Randomization, stack canaries, and exploit mitigation techniques

#operating-systems #aslr-stack-protection #computer-science

Assembly Language Basics: Writing Code the CPU Understands

Learn to read and write simple programs in x86 and ARM assembly, understanding registers, instructions, and the art of thinking in low-level operations.

#operating-systems #assembly-language-basics #computer-science

Boolean Logic & Gates

Understanding AND, OR, NOT gates and how they combine into arithmetic logic units — the building blocks of every processor.

#operating-systems #boolean-logic-gates #computer-science