02 Process and Thread Management

Table of Contents

Overview

Process and thread management are fundamental responsibilities of modern operating systems. A process represents an independent program execution unit with its own memory space and resources, while a thread is a lightweight execution unit within a process that shares the process's memory space with other threads.

Key Concepts

Process:

  • Independent execution unit representing a running program

  • Contains program code, data, heap, stack, and system resources

  • Isolated memory space provides protection between processes

  • Managed through Process Control Block (PCB) or Task Control Block (TCB)

  • Heavy-weight: significant overhead for creation and context switching

Thread:

  • Smallest unit of execution within a process

  • Shares process memory space, code, data, and heap with other threads

  • Has its own stack, registers, and program counter

  • Light-weight: minimal overhead for creation and context switching

  • Enables concurrent execution within a single process

Why Both Exist

Processes provide:

  • Strong isolation and fault tolerance

  • Security boundaries between different applications

  • Resource management at the application level

  • Protection from bugs in other processes

Threads provide:

  • Efficient parallelism within an application

  • Shared memory communication without IPC overhead

  • Responsive user interfaces (UI thread + worker threads)

  • Better resource utilization on multi-core systems

Process Fundamentals

An operating system process is a fundamental concept where each program's execution is represented as an independent unit. Processes allow the operating system to manage and execute multiple tasks concurrently, ensuring efficient resource use and isolation of programs from one another.

Components

┌─────────────────────────────────────────────────────────────┐
│                      Process Structure                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌───────────────────────────────────────────────────────┐  │
│  │           Process Control Block (PCB)                 │  │
│  │  • Process ID (PID)                                   │  │
│  │  • Process State (Running/Ready/Blocked)              │  │
│  │  • Program Counter                                    │  │
│  │  • CPU Registers                                      │  │
│  │  • Memory Management Info                             │  │
│  │  • I/O Status                                         │  │
│  └───────────────────────────────────────────────────────┘  │
│                                                             │
│  ┌───────────────────────────────────────────────────────┐  │
│  │              Memory Layout                            │  │
│  │                                                       │  │
│  │  High Address                                         │  │
│  │  ┌─────────────────────────────────────────────────┐  │  │
│  │  │            Stack (grows down ↓)                 │  │  │
│  │  │  • Local variables                              │  │  │
│  │  │  • Function parameters                          │  │  │
│  │  │  • Return addresses                             │  │  │
│  │  ├─────────────────────────────────────────────────┤  │  │
│  │  │                     ↕                           │  │  │
│  │  │              (Free Space)                       │  │  │
│  │  │                     ↕                           │  │  │
│  │  ├─────────────────────────────────────────────────┤  │  │
│  │  │            Heap (grows up ↑)                    │  │  │
│  │  │  • Dynamic allocations (malloc/new)             │  │  │
│  │  ├─────────────────────────────────────────────────┤  │  │
│  │  │            Data Section                         │  │  │
│  │  │  • Global variables                             │  │  │
│  │  │  • Static variables                             │  │  │
│  │  ├─────────────────────────────────────────────────┤  │  │
│  │  │            Code (Text) Section                  │  │  │
│  │  │  • Program instructions                         │  │  │
│  │  │  • Read-only                                    │  │  │
│  │  └─────────────────────────────────────────────────┘  │  │
│  │  Low Address                                          │  │
│  └───────────────────────────────────────────────────────┘  │
│                                                             │
│  ┌───────────────────────────────────────────────────────┐  │
│  │              File Descriptors                         │  │
│  │  • stdin (0), stdout (1), stderr (2)                  │  │
│  │  • Open files and I/O streams                         │  │
│  └───────────────────────────────────────────────────────┘  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Key Components of a Process:

  1. Program Code: The executable code that the CPU executes

  2. Process Control Block (PCB): Data structure storing process information

  3. Process State: Current state (running, ready, blocked, or terminated)

  4. Program Counter (PC): Keeps track of the next instruction

  5. CPU Registers: Store data and control process execution

  6. Stack: For local variables and function call management

  7. Data Section: Global and static variables

  8. Heap: For dynamic memory allocation

  9. File Descriptors: Access to files and I/O devices

  10. Parent-Child Relationship: Process hierarchy

  11. Inter-Process Communication (IPC): Channels for communication

  12. Signal Handlers: Registered signals and associated handlers

  13. Resource Usage: CPU time, memory, and other resource consumption

Process Control Block (PCB)

A process is represented using a Process Control Block (PCB) or Task Control Block (TCB), which is a data structure containing all information about a process.

PCB Contents:

  • Process state (running, ready, blocked, etc.)

  • Program counter and CPU registers

  • Stack pointer

  • Memory management information (page tables, segment tables)

  • Priority and scheduling information

  • Program code and data pointers

  • I/O status information

  • File descriptor table

  • Accounting information (CPU time used, time limits)

PCB Operations:

  • Stored in the kernel of the operating system

  • During context switch, current process state saved to its PCB

  • Next process state loaded from its PCB

  • Enables efficient process management and multitasking

Stack and Heap

The stack and heap are two distinct regions within a process's address space serving different purposes.

Stack

Purpose: Management of function call frames, including parameter passing, return addresses, and local variables. It is a last-in, first-out (LIFO) data structure.

Characteristics:

  • Fixed-size: Typically determined during process creation

  • Fast access: Simple pointer manipulation

  • Automatic allocation/deallocation: Implicit as functions are called and return

  • Limited lifetime: Variables tied to function scope

  • Thread-specific: Each thread has its own stack

  • Grows downward (typically) from high memory addresses

Use Cases:

  • Local variables

  • Function parameters

  • Return addresses

  • Function call frames

Heap

Purpose: Dynamic memory allocation where data can be allocated and deallocated at runtime. Suitable for data structures with non-deterministic lifetime.

Characteristics:

  • Variable size: Can grow or shrink during program execution

  • Slower access: Involves memory management overhead

  • Explicit allocation/deallocation: Programmer manages (malloc/free, new/delete)

  • Longer lifetime: Variables persist across function calls

  • Shared among threads: All threads in a process share the heap

  • Grows upward (typically) from low memory addresses

Use Cases:

  • Dynamic data structures (linked lists, trees)

  • Large objects

  • Objects with lifetime beyond function scope

  • Shared data between threads

Key Differences:

Aspect
Stack
Heap

Usage

Function call management

Dynamic memory allocation

Allocation

Automatic (implicit)

Explicit (programmer-managed)

Lifetime

Function scope

Arbitrary (programmer-controlled)

Speed

Fast (pointer manipulation)

Slower (memory management)

Scope

Thread-specific

Process-wide (shared)

Size

Fixed at creation

Variable during execution

Fragmentation

None

Can occur over time

Management

OS/compiler automatic

Programmer responsibility

Process Lifecycle

                    ┌─────────────────────┐
                    │                     │
                    │        NEW          │
                    │  (Being Created)    │
                    │                     │
                    └──────────┬──────────┘

                               │ admit

                    ┌─────────────────────┐
         ┌─────────>│                     │
         │          │       READY         │<──────────┐
         │          │  (Waiting for CPU)  │           │
         │          │                     │           │
         │          └──────────┬──────────┘           │
         │                     │                      │
         │           dispatch  │                      │
         │          (schedule) │                      │
         │                     ↓                      │
         │          ┌─────────────────────┐           │
         │          │                     │           │
         │ timeout  │      RUNNING        │           │
         │ (preempt)│   (Executing on     │           │
         └──────────┤       CPU)          │           │
                    │                     │           │
                    └──────┬───────┬──────┘           │
                           │       │                  │
                  I/O or   │       │ exit             │
                  event    │       │                  │
                  wait     │       ↓                  │
                           │   ┌─────────────────┐    │
                           │   │   TERMINATED    │    │
                           │   │   (Completed)   │    │
                           │   └─────────────────┘    │
                           │                          │
                           ↓                          │
                    ┌─────────────────────┐           │
                    │                     │  I/O or   │
                    │      BLOCKED        │  event    │
                    │     (Waiting)       │ complete  │
                    │                     │           │
                    └─────────────────────┘───────────┘

The process lifecycle refers to the various states and transitions that a process goes through during its existence. These states are managed by the operating system's process scheduler.

States

1. New: The process is being created by the operating system but has not yet started executing. Resources such as memory and data structures are being allocated.

2. Ready: The process is prepared to run but the CPU is currently allocated to another process. It waits in a ready queue to be executed when the CPU becomes available.

3. Running: The process is currently being executed by the CPU. It is the active state where the program's instructions are being executed.

4. Blocked (or Waiting): The process needs to wait for some event or resource to become available (e.g., user input, I/O completion). While blocked, the process does not consume CPU time.

5. Terminated (or Exit): The process has finished execution (successfully or due to error) and is being terminated. Resources are released and the process is removed from the system.

Transitions

  • New → Ready: Process creation completes, ready to run

  • Ready → Running: CPU becomes available, scheduler selects process

  • Running → Ready: Time quantum expires (preemption) or voluntary yield

  • Running → Blocked: Process waits for I/O or event

  • Blocked → Ready: I/O completes or event occurs

  • Running → Terminated: Process completes or is killed

The lifecycle is dynamic, with processes moving between states based on scheduling policies, external events, and program logic. Understanding and managing this lifecycle is crucial for efficient resource utilization and multitasking.

Process Categories

I/O-Bound Process

An I/O-bound process spends significant execution time performing input/output operations such as disk reads/writes, network communication, or user interaction.

Characteristics:

  • Frequently pauses execution waiting for I/O

  • Spends more time in blocked state than using CPU

  • Short CPU bursts between I/O operations

  • Benefits from higher scheduling priority for responsiveness

Examples:

  • Web servers handling client requests

  • Database servers with frequent disk access

  • Interactive applications waiting for user input

  • Text editors, IDEs

CPU-Bound Process

A CPU-bound process primarily requires CPU resources for computation and data processing, characterized by long and intense computations.

Characteristics:

  • Keeps CPU busy for extended periods

  • Minimal time waiting for I/O operations

  • Long CPU bursts

  • Benefits from longer time quantums

Examples:

  • Mathematical simulations

  • Video encoding and rendering

  • Scientific calculations

  • Cryptographic operations

  • Machine learning training

Scheduling Implications:

Different strategies are employed to handle these process types effectively:

  • I/O-bound: Priority and responsiveness to I/O operations are vital

  • CPU-bound: Fair distribution of CPU time and optimization of computational resources are key

Thread Fundamentals

Threads are the smallest unit of execution within a process, enabling concurrent and parallel programming within a single application.

Basic Concepts

1. Thread: An independent sequence of instructions that can be scheduled and run by the operating system's thread scheduler. A process can contain multiple threads executing concurrently and sharing the same process resources.

2. Multithreading: A technique where a program is divided into multiple threads that execute independently but share the same memory space, allowing concurrent execution of tasks within the same process.

3. Concurrency: Multiple tasks being executed in overlapping time periods, not necessarily simultaneously. Threads enable concurrency by allowing multiple tasks to make progress independently.

4. Parallelism: Multiple tasks executing at the same time, often on multiple CPU cores or processors. Threads can achieve parallelism, but not all multithreaded programs are inherently parallel.

5. Thread Safety: Code that behaves correctly and reliably when accessed by multiple threads simultaneously. Thread-safe code avoids data races and conflicts that occur in multithreaded environments.

6. Context Switching: The process of saving the state of a running thread, loading the state of another thread, and switching between them. Essential for the thread scheduler but incurs overhead.

7. Thread Priority: Many operating systems allow threads to have different priorities affecting scheduling order. Higher-priority threads receive preference from the scheduler.

8. Thread Lifecycle: Threads have a lifecycle including creation, running, and termination. They can be created, started, paused, resumed, and eventually terminated.

Thread Components

Each thread within a process has:

Thread-Specific:

  • Thread ID (TID)

  • Program Counter (PC)

  • Register set

  • Stack (for local variables and function calls)

  • Thread-local storage

  • State (running, ready, blocked, terminated)

Shared with Process:

  • Code section (program instructions)

  • Data section (global and static variables)

  • Heap (dynamically allocated memory)

  • Open file descriptors

  • Signal handlers

  • Process ID (PID)

Advantages of Threads

1. Resource Sharing: Threads share memory and resources of the process, making communication efficient without explicit IPC mechanisms.

2. Responsiveness: Multithreaded applications remain responsive; one thread can continue execution while others are blocked.

3. Economy: Thread creation and context switching are less expensive than process operations.

4. Scalability: Multithreaded programs can leverage multi-core processors for true parallelism.

5. Modularity: Different aspects of a program can be separated into threads (e.g., UI thread, worker threads, I/O threads).

Challenges with Threads

1. Race Conditions: Multiple threads accessing shared data simultaneously can lead to unpredictable results.

2. Deadlock: Two or more threads permanently blocked, each waiting for resources held by others.

3. Complexity: Designing, debugging, and testing multithreaded programs is more complex than single-threaded alternatives.

4. Synchronization Overhead: Coordinating threads through locks and other mechanisms adds overhead.

Process vs Thread Comparison

Aspect
Process
Thread

Definition

Independent program execution unit

Execution unit within a process

Memory Space

Separate address space

Shares process address space

Communication

IPC mechanisms (pipes, sockets, shared memory)

Direct memory access (shared variables)

Creation Overhead

High (heavy-weight)

Low (light-weight)

Context Switch

Expensive (full context)

Cheap (minimal context)

Isolation

Strong (protected from other processes)

Weak (shared memory with other threads)

Resource Usage

Each has own resources

Share process resources

Fault Tolerance

Crash doesn't affect other processes

Crash can bring down entire process

Synchronization

Not required between processes

Required for shared data access

Use Case

Independent applications

Parallel tasks within application

Examples

Browser, text editor, compiler

UI thread, worker thread, I/O thread

Multithreading Models

Multithreading models define the relationship between user-level threads (managed by libraries) and kernel-level threads (managed by the OS).

Many-to-One Model (User-Level Threads)

Multiple user-level threads (ULTs) map to a single kernel-level thread (KLT).

Characteristics:

  • ULTs managed entirely by user-level thread library

  • OS kernel unaware of ULTs

  • Thread management (creation, scheduling, synchronization) in user space

  • Fast thread operations (no kernel involvement)

Advantages:

  • Low overhead for thread operations

  • Flexible scheduling policies

  • Portable across operating systems

  • High degree of control for programmer

Disadvantages:

  • Cannot exploit multi-core processors (all ULTs share one KLT)

  • Blocking system call blocks entire process

  • No true parallelism

  • Poor scalability

Use Cases:

  • I/O-bound applications with many threads

  • Fine-grained parallelism scenarios

  • Legacy systems

Examples:

  • Green threads (older Java implementations)

  • User-level threading libraries

One-to-One Model (Kernel-Level Threads)

Each user-level thread corresponds to a separate kernel-level thread.

Characteristics:

  • Each ULT has its own KLT

  • OS kernel manages all threads

  • True parallelism across multiple cores

  • System calls by one thread don't block others

Advantages:

  • True parallelism on multi-core systems

  • Better concurrency

  • Blocking call doesn't block entire process

  • Well-suited for compute-bound tasks

Disadvantages:

  • Higher overhead (kernel involvement for all operations)

  • Thread creation is expensive

  • Limited scalability (kernel thread limit)

  • Context switching overhead

Use Cases:

  • Compute-bound applications

  • Applications requiring parallelism across cores

  • Modern general-purpose systems

Examples:

  • Windows threads

  • Linux threads (NPTL - Native POSIX Thread Library)

  • Modern threading libraries

Many-to-Many Model (Hybrid)

Multiple user-level threads map to smaller or equal number of kernel-level threads.

Characteristics:

  • Combines advantages of both models

  • Typically one KLT per processor core

  • ULTs managed by user-level libraries

  • KLTs managed by operating system

  • Dynamic mapping between ULTs and KLTs

Advantages:

  • Balances control and performance

  • True parallelism with low overhead

  • Can create many ULTs without kernel thread limit

  • Flexible scheduling

  • Blocking call doesn't block all threads

Disadvantages:

  • Complex implementation

  • Requires coordination between user and kernel schedulers

  • Potential scheduling conflicts

Use Cases:

  • Applications with variable thread counts

  • Systems requiring both performance and flexibility

  • Server applications with many concurrent tasks

Examples:

  • Solaris (older versions)

  • Some modern runtime systems (Go, Erlang)

Model Selection

The choice of multithreading model depends on:

  • Application characteristics (I/O-bound vs. CPU-bound)

  • Target platform capabilities

  • Performance requirements

  • Scalability needs

  • Development complexity constraints

Modern systems often use the one-to-one model due to improvements in kernel efficiency and the prevalence of multi-core processors. Some runtime environments implement many-to-many models in user space for specialized workloads.

Scheduling

Scheduling determines the order in which processes and threads execute on the CPU, balancing fairness, responsiveness, and resource utilization.

Context Switch

A context switch is the process by which an operating system saves the state of a running process/thread and restores the state of another to share a single CPU among multiple execution units.

Context Switch Process:

  1. Interrupt Handling:

    • Timer interrupt (time quantum expires)

    • Hardware interrupt (I/O completion)

    • Software interrupt (system call)

  2. Save Current Context:

    • CPU switches from user mode to kernel mode

    • Interrupt service routine (ISR) executes

    • Current process/thread context saved to PCB/TCB:

      • Program counter

      • Register values

      • CPU state information

  3. Choose New Process/Thread:

    • Scheduler selects next process/thread from ready queue

    • Selection based on scheduling algorithm (Round Robin, Priority, etc.)

    • Considers priority and other criteria

  4. Load New Context:

    • Saved state of selected process/thread loaded from PCB/TCB

    • Program counter, registers, and CPU state restored

    • CPU switches back to user mode

  5. Resume Execution:

    • CPU resumes execution of new process/thread

    • Execution continues from where it was last interrupted

Context Switch Overhead:

  • Direct costs: Saving/loading registers, updating data structures

  • Indirect costs: Cache misses, TLB flushes, pipeline stalls

  • Time varies from microseconds to milliseconds

Efficient context switching is performance-critical, affecting system responsiveness and resource utilization. Operating systems minimize this overhead through optimization techniques.

Scheduling Algorithms

Scheduling algorithms determine which process or thread runs next, each with distinct characteristics suited to different scenarios.

First-Come, First-Served (FCFS)

Overview: The simplest scheduling algorithm where processes execute in the order they arrive in the ready queue.

Characteristics:

  • Non-preemptive

  • FIFO queue implementation

  • Simple to implement

Advantages:

  • Fair in arrival order

  • No starvation

  • Low overhead

Disadvantages:

  • High variance in response time

  • Convoy effect (short processes wait for long ones)

  • Poor for interactive systems

Application Scenario: Batch processing systems where arrival order is significant and execution times are unpredictable.

Shortest Job Next (SJN) / Shortest Job First (SJF)

Overview: Schedules processes based on expected burst times, with shortest job executed first.

Characteristics:

  • Can be preemptive (SRTF - Shortest Remaining Time First) or non-preemptive

  • Requires knowledge of burst times

  • Optimal for minimizing average waiting time

Advantages:

  • Minimizes average waiting time

  • Optimal when burst times known

Disadvantages:

  • Burst times often unknown in advance

  • Can cause starvation for long processes

  • Impractical for most interactive systems

Application Scenario: Environments where job lengths are known (batch systems) or can be accurately estimated.

Round Robin (RR)

Overview: Each process receives a fixed time quantum (time slice). When the time slice expires, the process moves to the end of the ready queue.

Characteristics:

  • Preemptive

  • Time quantum typically 10-100ms

  • Circular queue of ready processes

Advantages:

  • Fair CPU time allocation

  • Good response time for interactive systems

  • No starvation

  • Prevents process monopolization

Disadvantages:

  • Performance depends on time quantum size

  • Higher context switch overhead

  • Not optimal for batch processing

Application Scenario: Time-sharing systems, interactive environments, and ensuring fair CPU allocation. Ideal for preventing any single process from monopolizing CPU.

Priority Scheduling

Overview: Each process assigned a priority level; highest priority process executes first.

Characteristics:

  • Can be preemptive or non-preemptive

  • Priorities can be static or dynamic

  • May use priority queues

Advantages:

  • Important tasks execute promptly

  • Flexible (can implement various policies)

  • Good for real-time systems

Disadvantages:

  • Can cause starvation for low-priority processes

  • Priority inversion possible

  • Requires careful priority assignment

Solutions to Starvation:

  • Aging: Gradually increase priority of waiting processes

  • Dynamic priority adjustment

Application Scenario: Real-time systems and environments where critical tasks must execute promptly. Effective when processes have varying importance levels.

Multilevel Queue Scheduling

Overview: Processes categorized into multiple queues based on priority or other attributes. Each queue can use its own scheduling algorithm.

Characteristics:

  • Multiple queues with different priorities

  • Processes assigned to queues based on attributes

  • Each queue has its own scheduling algorithm

  • Fixed priority between queues

Advantages:

  • Different process types managed separately

  • Flexible (different algorithms per queue)

  • Can prioritize certain process types

Disadvantages:

  • Processes cannot move between queues

  • Potential starvation for low-priority queues

  • Complex configuration

Application Scenario: Scenarios where different process classes need separate management (e.g., system processes, interactive processes, batch processes).

Multilevel Feedback Queue Scheduling

Overview: Similar to multilevel queue scheduling, but processes can move between queues based on their behavior and characteristics.

Characteristics:

  • Multiple queues with different priorities

  • Processes can change queues dynamically

  • Typically uses Round Robin within queues

  • Aging mechanism to prevent starvation

Advantages:

  • Adapts to changing workload

  • Prevents starvation through aging

  • Rewards short jobs

  • Flexible and responsive

Disadvantages:

  • Complex implementation

  • Requires tuning of parameters

  • Higher overhead

Application Scenario: Dynamic environments with varying workload characteristics. Common in modern general-purpose operating systems (Linux, Windows).

Scheduling Goals

Different systems optimize for different metrics:

Goal
Description
Important For

CPU Utilization

Keep CPU busy

All systems

Throughput

Processes completed per unit time

Batch systems

Turnaround Time

Time from submission to completion

Batch systems

Waiting Time

Time spent in ready queue

Interactive systems

Response Time

Time from request to first response

Interactive systems

Fairness

All processes get fair CPU time

General-purpose systems

Predictability

Consistent performance

Real-time systems

Inter-Process Communication

Inter-process communication (IPC) encompasses mechanisms for processes to communicate and share data. Common IPC methods include pipes, message queues, shared memory, sockets, and remote procedure calls.

IPC Methods Overview

Method
Description
Use Case

Pipes

One-way communication between related processes

Parent-child communication

Named Pipes (FIFOs)

Named file system entry for unrelated processes

Unrelated process communication

Message Queues

Structured asynchronous message passing

Reliable inter-process data exchange

Shared Memory

Common memory region accessible to processes

High-speed data sharing

Sockets

Network-based communication

Local or network communication

Signals

Asynchronous event notification

Process control and notification

Semaphores

Synchronization primitive

Resource access control

File-based IPC

Communication through files

Simple data sharing and persistence

RPC

Remote procedure call mechanism

Distributed systems, client-server

Message Queues

Message queues provide structured, asynchronous message passing between processes with reliable delivery.

Characteristics:

  • Messages stored in kernel queue

  • FIFO or priority-based ordering

  • Asynchronous communication

  • Persistent until read

POSIX Message Queue Example:

#include <mqueue.h>

#define QUEUE_NAME "/my_message_queue"
#define MAX_MSG_SIZE 100

int main() {
    mqd_t mq;
    struct mq_attr attr;
    char message[MAX_MSG_SIZE];
    
    // Define message queue attributes
    attr.mq_flags = 0;
    attr.mq_maxmsg = 10;
    attr.mq_msgsize = MAX_MSG_SIZE;
    
    // Create or open the message queue
    mq = mq_open(QUEUE_NAME, O_CREAT | O_RDWR, 0666, &attr);
    if (mq == (mqd_t)-1) {
        perror("mq_open");
        exit(1);
    }
    
    // Send a message
    strcpy(message, "Hello from Process 1!");
    if (mq_send(mq, message, strlen(message), 0) == -1) {
        perror("mq_send");
        exit(1);
    }
    printf("Message sent: %s\n", message);
    
    // Receive a message
    unsigned int prio;
    ssize_t msg_len = mq_receive(mq, message, MAX_MSG_SIZE, &prio);
    if (msg_len == -1) {
        perror("mq_receive");
        exit(1);
    }
    message[msg_len] = '\0';
    printf("Received message: %s\n", message);
    
    // Close and optionally unlink the queue
    mq_close(mq);
    // mq_unlink(QUEUE_NAME);  // Remove queue
    
    return 0;
}

Shared Memory

Shared memory allows multiple processes to access a common memory region, providing the fastest IPC method.

Characteristics:

  • Fastest IPC method (no data copying)

  • Requires synchronization (semaphores, mutexes)

  • Processes map same physical memory

  • Direct memory access

POSIX Shared Memory Example:

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>

#define SHARED_MEMORY_NAME "/my_shared_memory"
#define SHARED_MEMORY_SIZE 4096

int main() {
    int shm_fd;
    void* shared_memory;
    char* shared_message = "Hello from Process 1!";
    
    // Create or open the shared memory object
    shm_fd = shm_open(SHARED_MEMORY_NAME, O_CREAT | O_RDWR, 0666);
    if (shm_fd == -1) {
        perror("shm_open");
        exit(1);
    }
    
    // Set the size of the shared memory
    if (ftruncate(shm_fd, SHARED_MEMORY_SIZE) == -1) {
        perror("ftruncate");
        exit(1);
    }
    
    // Map the shared memory into process address space
    shared_memory = mmap(0, SHARED_MEMORY_SIZE, 
                         PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (shared_memory == MAP_FAILED) {
        perror("mmap");
        exit(1);
    }
    
    // Write to shared memory
    strcpy(shared_memory, shared_message);
    printf("Message written: %s\n", (char*)shared_memory);
    
    // Unmap and close
    munmap(shared_memory, SHARED_MEMORY_SIZE);
    close(shm_fd);
    
    // Optionally remove the shared memory object
    // shm_unlink(SHARED_MEMORY_NAME);
    
    return 0;
}

Remote Procedure Call (RPC)

RPC allows programs to execute procedures on remote systems as if they were local function calls.

gRPC Example Overview:

  1. Define Service (.proto file):

syntax = "proto3";

service Calculator {
    rpc CalculateSquare (Number) returns (Result);
}

message Number {
    int32 value = 1;
}

message Result {
    int32 value = 1;
}
  1. Generate Code:

protoc -I=. --cpp_out=. calculator.proto
protoc -I=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` calculator.proto
  1. Implement Server:

class CalculatorServiceImpl final : public Calculator::Service {
public:
    ::grpc::Status CalculateSquare(::grpc::ServerContext* context, 
                                   const ::Number* request, 
                                   ::Result* response) override {
        response->set_value(request->value() * request->value());
        return ::grpc::Status::OK;
    }
};
  1. Implement Client:

int main() {
    auto channel = grpc::CreateChannel("localhost:50051", 
                                       grpc::InsecureChannelCredentials());
    auto stub = Calculator::NewStub(channel);
    
    Number number;
    number.set_value(5);
    Result result;
    
    grpc::ClientContext context;
    grpc::Status status = stub->CalculateSquare(&context, number, &result);
    
    if (status.ok()) {
        std::cout << "Square: " << result.value() << std::endl;
    }
    return 0;
}

Signals and Interrupts

Overview

Signals and interrupts are mechanisms for handling asynchronous events, but they serve different purposes and operate at different levels.

Signal:

  • Software interrupt delivered to a process/thread

  • Notifies process of specific events or conditions

  • Generated within software

  • Used for inter-process communication and event notification

  • Examples: SIGINT (Ctrl+C), SIGTERM (termination), SIGCHLD (child process event)

Interrupt:

  • Hardware signal sent to CPU

  • Alerts CPU to events requiring immediate attention

  • Generated by external hardware devices

  • Used for handling external events and I/O operations

  • Examples: Timer interrupt, keyboard interrupt, network card interrupt

Key Differences:

Aspect
Signal
Interrupt

Source

Software (processes, OS)

Hardware (devices, timers)

Purpose

Software event notification, IPC

Hardware event handling

Initiation

Programmatic or manual

Hardware device timing/completion

Handling

Process signal handlers

OS interrupt service routines

Level

Process/application level

System/hardware level

Signal Handling

Operating systems provide mechanisms for processes to handle signals, defining how they respond to various events.

Signal Generation Events:

  • User actions (Ctrl+C generates SIGINT)

  • Hardware exceptions (division by zero, invalid memory access)

  • I/O events (data arrival, socket activity)

  • Timer and alarm signals

  • Other processes (kill command)

Signal Delivery: Signals are delivered to target processes based on the event type:

  • User-generated signals directed to specific process

  • Hardware exceptions delivered to offending process

  • Broadcast signals to process groups

Signal Handling Options:

  1. Termination: Process terminates (default for many signals)

  2. Ignore: Process ignores the signal entirely

  3. Custom Handler: Execute programmer-defined signal handler function

  4. Default Action: Take predefined action (varies by signal type)

Common Signals:

Signal
Number
Default Action
Description

SIGINT

2

Terminate

Interrupt (Ctrl+C)

SIGQUIT

3

Core dump

Quit (Ctrl+)

SIGKILL

9

Terminate

Force kill (cannot be caught)

SIGSEGV

11

Core dump

Segmentation fault

SIGTERM

15

Terminate

Termination request

SIGCHLD

17

Ignore

Child process status change

SIGSTOP

19

Stop

Stop process (cannot be caught)

SIGCONT

18

Continue

Continue if stopped

Signal Features:

  • Signal Queuing: Signals can be queued if they arrive while handling another signal

  • Blocking Signals: Processes can temporarily block signals to protect critical sections

  • Signal Masks: Each process has a signal mask defining which signals are blocked

  • Reliable vs. Unrealizable Signals: Modern systems use reliable signals that aren't lost

Interrupt Handling

Operating systems handle interrupts through hardware and software cooperation to ensure timely response to external events.

Interrupt Handling Process:

  1. Interrupt Generation:

    • Hardware devices generate interrupts (I/O controllers, timers)

    • Software can generate software interrupts (system calls, exceptions)

  2. Interrupt Request (IRQ):

    • Device sends Interrupt Request to CPU

    • Each interrupt type has specific IRQ line

    • Priority assigned to different IRQ lines

  3. CPU Response:

    • CPU suspends current execution

    • Enters kernel mode (supervisor mode)

    • Saves program counter and register state

    • Allows return to interrupted code later

  4. Interrupt Handling:

    • CPU transfers control to Interrupt Service Routine (ISR)

    • Each interrupt type has predefined ISR

    • ISR identifies interrupt source

    • Processes event and performs necessary actions

  5. Context Switch (if needed):

    • For timer interrupts or I/O completion

    • May switch to different process/thread

    • Saves current process state

    • Loads new process state

  6. Interrupt Acknowledgment:

    • CPU acknowledges interrupt controller

    • Indicates interrupt processing complete

    • May send signal to device or clear flag

  7. Return from Interrupt:

    • ISR completes execution

    • CPU restores previously saved state

    • Resumes interrupted program execution

Interrupt Management:

  • Interrupt Prioritization: Higher-priority interrupts can preempt lower-priority ones

  • Masking/Unmasking: OS can disable/enable specific interrupts

  • Nested Interrupts: Higher-priority interrupts can interrupt ISRs

  • Interrupt Latency: Time between interrupt occurrence and ISR execution

Design Patterns

Multithreaded programming design patterns help structure and manage concurrent tasks efficiently, solving specific concurrency problems.

Boss-Worker (Master-Slave)

Description: One thread (boss) delegates tasks to a pool of worker threads. The boss divides work, distributes tasks, and collects results.

Characteristics:

  • Boss thread manages work distribution

  • Worker pool executes tasks

  • Task queue between boss and workers

  • Results collected by boss or written to shared location

Use Cases:

  • Parallelizing independent tasks

  • Web server request handling

  • Task distribution in compute clusters

  • Managing thread pool efficiently

Advantages:

  • Efficient core utilization

  • Scalable (add more workers)

  • Boss can implement load balancing

  • Clear separation of concerns

Disadvantages:

  • Boss can become bottleneck

  • Overhead of task distribution

  • Worker synchronization needed

Pipeline

Description: A series of processing stages connected in linear sequence. Each stage processes data and passes it to the next stage.

Characteristics:

  • Multiple stages in sequence

  • Each stage performs specific operation

  • Data flows through pipeline

  • Stages can run concurrently

Use Cases:

  • Stream processing

  • Data transformation tasks

  • Assembly-line style processing

  • Compiler phases (lexing, parsing, code generation)

  • Image/video processing

Advantages:

  • Natural decomposition for sequential processing

  • Good throughput with multiple items in pipeline

  • Each stage can be optimized independently

  • Parallelism across stages

Disadvantages:

  • Slowest stage determines throughput

  • Complex synchronization between stages

  • Limited parallelism for single items

Layered Design

Description: System divided into multiple layers, each responsible for specific functionality aspect. Threads manage each layer.

Characteristics:

  • Hierarchical organization

  • Each layer provides services to layer above

  • Threads coordinate between layers

  • Clear abstraction boundaries

Use Cases:

  • Network protocols (OSI model: data link, network, transport, application)

  • Graphics rendering pipelines

  • Operating system layers

  • Multi-tier applications

Advantages:

  • Clear separation of concerns

  • Modularity and maintainability

  • Each layer can be tested independently

  • Facilitates distributed implementation

Disadvantages:

  • Performance overhead from layer crossings

  • Complexity in layer coordination

  • Potential bottlenecks at layer boundaries

Producer-Consumer

Description: One or more producer threads create items/data, and one or more consumer threads process these items.

Characteristics:

  • Shared buffer between producers and consumers

  • Synchronization using semaphores or condition variables

  • Producers add items to buffer

  • Consumers remove items from buffer

Use Cases:

  • Separating data generation from processing

  • Event handling systems

  • Logging systems

  • I/O buffering

Advantages:

  • Decouples production from consumption

  • Producers and consumers work at their own pace

  • Buffer smooths rate differences

  • Natural parallelism

Disadvantages:

  • Buffer size management

  • Synchronization overhead

  • Potential deadlock if not designed carefully

Reader-Writer

Description: Multiple threads read shared resource simultaneously (readers), while ensuring exclusive access for single thread during writes (writer).

Characteristics:

  • Multiple concurrent readers allowed

  • Single exclusive writer

  • Readers and writers mutually exclusive

  • Synchronization using read-write locks

Use Cases:

  • Read-heavy workloads

  • Databases with frequent queries

  • Configuration data

  • Caching systems

Advantages:

  • Maximizes concurrency for readers

  • Better performance than exclusive locks for read-heavy workloads

  • Clear semantics

Disadvantages:

  • Writer starvation possible

  • More complex than simple locks

  • Fairness considerations

Thread Pool

Description: Pool of pre-created worker threads execute tasks from a queue, avoiding overhead of thread creation/destruction.

Characteristics:

  • Fixed or dynamic number of worker threads

  • Task queue feeding workers

  • Threads reused for multiple tasks

  • Manager coordinates pool

Use Cases:

  • Web servers handling requests

  • Parallel computing frameworks

  • Short-lived frequent tasks

  • Resource-constrained environments

Advantages:

  • Eliminates thread creation overhead

  • Limits concurrent threads (resource control)

  • Better resource utilization

  • Faster task execution start

Disadvantages:

  • Pool size tuning required

  • Potential thread starvation

  • Task queue management overhead

Performance

Metrics

Thread performance metrics evaluate efficiency, effectiveness, and scalability of multithreaded applications.

Key Metrics:

Metric
Description
Importance

Throughput

Tasks completed per unit time

System capacity

Latency

Time for single task completion

Responsiveness

Concurrency Level

Number of threads running concurrently

Resource utilization

CPU Utilization

Percentage of time CPU executes threads

Efficiency

Scalability

Performance improvement with additional cores

Parallel efficiency

Contention

Thread competition for resources

Bottleneck identification

Lock Contention

Frequency/duration of lock waiting

Synchronization overhead

Context Switching

Frequency of context switches

Overhead

Cache Efficiency

Cache hit rate and locality

Memory performance

Response Time Variability

Consistency of response times

Predictability

Additional Considerations:

  • Speedup: Performance improvement with N threads vs. 1 thread

  • Efficiency: Speedup / N (ideal is 1.0)

  • Amdahl's Law: Theoretical maximum speedup limited by serial portion

  • Resource Utilization: Memory, network, disk usage

  • Contention Resolution Time: Time to resolve resource conflicts

Programming Model Comparison

Aspect
Multithreading
Multiprocessing
Event-Driven

Execution Units

Multiple threads in process

Multiple independent processes

Single/few threads with event loop

Memory

Shared address space

Separate address spaces

Shared within thread

Communication

Direct memory access

IPC (pipes, sockets, shared memory)

Callbacks, event queue

Overhead

Low (lightweight)

High (heavy-weight)

Very low

Isolation

Weak

Strong

N/A

Fault Tolerance

Crash affects entire process

Crashes isolated

Depends on implementation

Synchronization

Required (locks, semaphores)

Optional (if using shared memory)

Minimal (single thread)

Scalability

Good (limited by Amdahl's Law)

Excellent (process isolation)

Excellent (for I/O-bound)

Best For

Concurrent tasks, shared data

Independent tasks, CPU-bound

I/O-bound, many connections

Examples

Web server threads, parallel algorithms

Chrome browser tabs, worker pools

Node.js, nginx, GUI event loops

When to Use:

  • Multithreading: Shared data, moderate concurrency, responsive UIs

  • Multiprocessing: CPU-intensive tasks, strong isolation needed, true parallelism

  • Event-Driven: High concurrency I/O operations, network services, low latency

References

Course Materials:

  • CS 6200: Introduction to Operating Systems - Georgia Tech OMSCS

Textbooks:

  • Arpaci-Dusseau and Arpaci-Dusseau, Operating Systems: Three Easy Pieces

Last updated