# Lecture 2 - Processes: Virtualizing the CPU Source: https://wisconsin.twango.dev/sp26-cs537/lectures/lecture-02 # Lecture 2 - Processes: Virtualizing the CPU **Instructor:** Michael Swift & Tej Chajed **Topics:** Processes, process state, CPU virtualization, limited direct execution, context switching > [!abstract]- Learning Outcomes > - Describe the process abstraction > - Argue why limited direct execution is a good approach for CPU virtualization > - Reproduce the mechanism for limited direct execution --- ## What is a Process? > **Process:** An execution stream in the context of a process state. > **Execution stream:** A stream of executing instructions; a running piece of code; a "thread of control." > > **Process state:** Everything that the running code can affect or be affected by. Process state includes: - **Registers** — general purpose, floating point, status, program counter, stack pointer - **Address space** — heap, stack, and code - **Open files** A process is different from a program: - **Program:** Static code and static data (lives on disk) - **Process:** Dynamic instance of code and data (lives in memory) Can have multiple process instances of the same program (e.g., many users running `ls` at the same time). --- ## Process Creation > **Process Creation:** > > 1. Program sits on disk (code + static data) > 2. OS loads code and static data into memory > 3. OS allocates heap and stack in the address space > 4. Process is ready to execute ### Two Ways to Create a Process 1. **Build a new empty process from scratch** 2. **Copy an existing process and change it appropriately** ### Option 1: New Process from Scratch - Steps: - Load specified code and data into memory; create empty call stack - Create and initialize PCB (make look like context-switch) - Put process on ready list - Advantages: No wasted work - Disadvantages: Difficult to setup process correctly and to express all possible options - Process permissions, where to write I/O, environment variables - Example: Windows has call with 10 arguments ### Option 2: Clone Existing Process and Change - Example: Unix `fork()` and `exec()` - `fork()`: Clones calling process - `exec(char *file)`: Overlays file image on calling process - `fork()`: - Stop current process and save its state - Make copy of code, data, stack, and PCB - Add new PCB to ready list - `exec(char *file)`: - Replace current data and code segments with those in specified file - Advantages: Flexible, clean, simple - Disadvantages: Wasteful to perform copy and then overwrite memory ### Unix Shell Implementation ```c while (1) { char *cmd = getcmd(); int retval = fork(); if (retval == 0) { // This is the child process // Setup the child's process environment here // E.g., where is standard I/O, how to handle signals? exec(cmd); // exec does not return if it succeeds printf("ERROR: Could not execute %s\n", cmd); exit(1); } else { // This is the parent process; Wait for child to finish int pid = retval; wait(pid); } } ``` --- ## Virtualizing the CPU Goal: Give each process the impression it alone is actively using the CPU. | Resource | Sharing Strategy | |----------|-----------------| | CPU | **Time sharing** | | Memory | **Space** sharing (later) | | Disk | **Space** sharing (later) | > [!note] Lecture Note > Time sharing on the physical CPU means processes take turns: A, B, A, B, A... Each process thinks it has its own virtual CPU. Memory and disk use space sharing — each process gets its own region. ### Process States ```mermaid stateDiagram-v2 running --> ready : deschedule ready --> running : schedule running --> blocked : I/O issued blocked --> ready : I/O complete ``` - **Running:** On the CPU (only one on a uniprocessor) - **Ready:** Waiting for the CPU - **Blocked:** Asleep — waiting for I/O or synchronization to complete > [!note] Lecture Note > When B is running, A sits in the ready state. The scheduler picks from the ready queue. OS must track every process in the system: - Each process identified by unique **Process ID (PID)** OS maintains queues of all processes: - **Ready queue:** Contains all ready processes - **Event queue:** One logical queue per event - e.g., disk I/O and locks - Contains all processes waiting for that event to complete --- ## Limited Direct Execution ### Direct Execution - Allow user process to run directly on hardware - OS creates process and transfers control to starting point (i.e., `main()`) ### Problems with Direct Execution 1. **Process could do something restricted** — could read/write other process data (disk or memory) 2. **Process could run forever** (slow, buggy, or malicious) — OS needs to be able to switch between processes 3. **Process could do something slow** (like I/O) — OS wants to use resources efficiently and switch CPU to other process Solution: **Limited direct execution** — OS and hardware maintain some control. --- ## Problem 1: Restricting Execution How can we ensure user process can't harm others? Solution: **privilege levels supported by hardware** (bit of status) - User processes run in **user mode** (restricted mode) - OS runs in **kernel mode** (not restricted) - Instructions for interacting with devices - Could have many privilege levels (advanced topic) How can a process access a device? - **System calls** (function call implemented by OS) - Change privilege level through **syscall trap instruction** ### What User Processes Cannot Do User processes are not allowed to perform: - General memory access - Disk I/O - Special x86 instructions like `lidt` If a process tries to do something restricted, the hardware generates a fault/exception. ### How System Calls Work 1. Process P wants to call `read()` — but P can only see its own memory because of **user mode** (other areas, including kernel, are hidden) 2. P cannot call kernel code directly — instead it uses a **syscall trap instruction** 3. The `read()` library function executes: `movl $6, %eax; syscall` - `$6` is the **syscall-table index** identifying which system call to invoke - `syscall` is the **trap instruction** that calls through the trap table 4. The `syscall` instruction switches to **kernel mode** — now the kernel can access all memory 5. The kernel follows entries in the trap table to the correct system call handler (e.g., `sys_read`) 6. The kernel can access user memory to fill in the user buffer 7. **Return-from-trap** at end to return to Process P (back in user mode) --- ## Problem 2: How to Take CPU Away? OS requirements for **multiprogramming** (or multitasking): - **Mechanism** — to switch between processes - **Policy** — to decide which process to schedule when ### Separation of Policy and Mechanism Reoccurring theme in OS: - **Policy:** Decision-maker to optimize some workload performance metric - Which process when? - Process **Scheduler** (future lecture) - **Mechanism:** Low-level code that implements the decision - How? - Process **Dispatcher** (this lecture) ### Dispatch Mechanism OS runs a **dispatch loop**: ```text while (1) { run process A for some time-slice stop process A and save its context load context of another process B } ``` > [!note] Lecture Note > The stop + save + load is called a **context switch**. Each process runs for a time slice (e.g., A, B, A, B...). Two key questions: 1. How does the dispatcher gain control? 2. What execution context must be saved and restored? ### Q1: How Does the Dispatcher Get Control? #### Option 1: Cooperative Multi-tasking - Trust process to relinquish CPU to OS through traps - Examples: System call, page fault (access page not in main memory), or error (illegal instruction or divide by zero) - Provide special `yield()` system call - Problem: Processes can misbehave - By avoiding all traps and performing no I/O, can take over entire machine - Only solution: Reboot! - Not performed in modern operating systems #### Option 2: True Multi-tasking - Guarantee OS can obtain control periodically - Enter OS by enabling periodic alarm clock - Hardware generates **timer interrupt** (CPU or separate chip) - Example: Every 10ms - User must not be able to mask timer interrupt - Dispatcher counts interrupts between context switches - Example: Waiting 20 timer ticks gives 200 ms time slice - Common time slices range from 10 ms to 200 ms ### Q2: What Context Must Be Saved? Dispatcher must track context of process when not running: - Save context in **process control block (PCB)** (or, process descriptor) What information is stored in PCB: - PID - Process state (i.e., running, ready, or blocked) - Execution state (all registers, PC, stack ptr) - Scheduling priority - Accounting information (parent and child processes) - Credentials (which resources can be accessed, owner) - Pointers to other allocated resources (e.g., open files) > [!note] Lecture Note > The PCB is a struct (e.g., `struct proc { pid, status, ... }`) that holds everything the OS needs to know about a process. Requires special **hardware support**: - Hardware saves process PC and PSR on interrupts ### Context Switch Procedure ```mermaid sequenceDiagram participant Program as Program participant HW as Hardware participant OS as Operating System Note over Program: Process A running... HW->>HW: timer interrupt HW->>HW: save regs(A) to k-stack(A) HW->>HW: move to kernel mode HW->>OS: jump to trap handler OS->>OS: Handle the trap OS->>OS: Call switch() routine OS->>OS: save regs(A) to proc-struct(A) OS->>OS: restore regs(B) from proc-struct(B) OS->>OS: switch to k-stack(B) OS->>HW: return-from-trap (into B) HW->>HW: restore regs(B) from k-stack(B) HW->>HW: move to user mode HW->>Program: jump to B's IP Note over Program: Process B running... ``` > [!note] Lecture Note > A timer interrupt (or syscall trap) causes hardware to save registers to the kernel stack and switch to kernel mode. The OS then performs a software context switch — saving A's registers to A's PCB and restoring B's from B's PCB. Finally, return-from-trap restores B's registers from B's kernel stack and jumps to B in user mode. --- ## Summary - **Virtualization:** Context switching gives each process the impression it has its own CPU - **Direct execution** makes processes fast - **Limited execution** at key points ensures OS retains control - Hardware provides a lot of OS support: - User vs kernel mode (privilege bit) - Timer interrupts - Automatic register saving and return-from-trap --- ## Related - **Navigation:** [[lecture-01|← Lecture 1]] · [[lecture-03|Lecture 3 →]] - **Exam:** [[exams/midterm-1/index|Midterm 1]] - **Course:** [[README|CS 537 Map of Content]]