Lecture 5 - Virtualization: CPU to Memory
Instructor: Michael Swift Topics: CPU virtualization recap, memory virtualization, address spaces, static relocation, dynamic relocation, base and bounds, segmentation
Learning Outcomes
- Recap of scheduling policies
- What is the need for memory virtualization?
- How to virtualize memory?
Recap: CPU Virtualization
Scheduling Mechanism
- Process: Abstraction to virtualize CPU
- Use time-sharing in OS to switch between processes
- Limited Direct Execution:
- Use system calls to access devices etc. from user mode
- Context-switch using interrupts for multi-tasking
CPU Summary
- Mechanism
- Process abstraction
- System call for protection
- Context switch to time-share
- Policy
- Metrics: turnaround time, response time
- Balance using MLFQ
Motivation for Memory Virtualization
Uniprogramming: One process runs at a time — OS occupies the top of physical memory, user process occupies the rest.
Disadvantages:
- Only one process runs at a time
- Process can destroy OS
Multiprogramming Goals
- Transparency: Process is unaware of sharing
- Protection: Cannot corrupt OS or other process memory
- Efficiency: Do not waste memory or slow down processes
- Sharing: Enable sharing between cooperating processes
Address Space Abstraction
The address space is the OS abstraction for a process’s view of memory. Each process has its own address space laid out in physical memory alongside other processes.
Lecture Note
Multiple processes are placed in different regions of physical memory. The right side of the diagram shows processes C, B, and A interleaved with free regions in a 512KB physical memory layout.
What is in the Address Space?
- Static: Code and some global variables
- Dynamic: Stack and heap
| Segment | Contents | Growth Direction |
|---|---|---|
| Code (top, 0KB–1KB) | Instructions (the code segment) | Fixed |
| Heap (below code) | malloc()’d data, dynamic data structures | Grows downward |
| Stack (bottom, 15KB–16KB) | Local variables, arguments, return values | Grows upward (toward lower addresses) |
Lecture Note
Static variables live in the code/data region at the top of the address space. The heap and stack grow toward each other in the free space between them.
Stack Organization
- Stack grows downward (toward lower addresses)
- A pointer separates allocated and free space
- Allocate: Decrement pointer
- Free: Increment pointer
- No fragmentation — allocation and deallocation follow LIFO order
What Goes on the Stack?
main () {
int A = 0;
foo(A);
printf("A: %d\n", A);
}
void foo (int Z) {
int A = 2;
Z = 5;
printf("A: %d Z: %d\n", A, Z);
}
Lecture Note
The stack frame for
mainis at the base. Whenfoois called, its frame (containing localAand parameterZ) is pushed on top. Theprintfarguments are pushed above that. The stack pointer moves as frames are pushed/popped.
Heap Organization
Allocate from any random location: malloc(), new(), etc.
- Heap memory consists of allocated and free areas (holes)
- Order of allocation and free is unpredictable
Lecture Note
Fragmentation occurs — free space exists between allocated blocks but may not be usable for new allocations because the free chunks are too small or non-contiguous.
Memory Access
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int x;
x = x + 3;
}
Compiled to assembly:
0x10: movl -0x8(%rbp), %rdi
0x13: addl $0x3, %rdi
0x19: movl %rdi, -0x8(%rbp)
%rbpis the base pointer — points to base of current stack frame%ripis the instruction pointer (program counter)
Example
With initial
%rip = 0x10,%rbp = 0x200:
- Fetch instruction at addr
0x10→ Exec: load from addr0x1f8(0x200 - 0x8)- Fetch instruction at addr
0x13→ Exec: no memory access (register add)- Fetch instruction at addr
0x19→ Exec: store to addr0x1f8
Memory Practice
int x;
int main(int argc, char *argv[]) {
int y;
int* z = malloc(sizeof(int));
}
| Address | Location |
|---|---|
x | Static data/code |
main | Static data/code |
y | Stack |
z | Stack |
*z | Heap |
How to Virtualize Memory
Problem: How to run multiple processes simultaneously when addresses are “hardcoded” into process binaries?
Possible solutions:
- Time sharing
- Static relocation
- Base (dynamic relocation)
- Base+Bounds
- Segmentation
Approach 1: Time Sharing Memory
Swap entire process memory to/from disk on each context switch.
Lecture Note
This is called swapping — writing the entire process state out to disk and loading the next process in. One process uses RAM at a time.
Problem: Ridiculously poor performance — copying entire memory to/from disk on every switch is far too slow.
Better alternative: Space sharing — at the same time, the space of memory is divided across processes. All remaining solutions use space sharing.
Approach 2: Static Relocation
Idea: OS rewrites each program before loading it as a process in memory. Each rewrite for a different process uses different addresses and pointers.
Changes jumps, loads of static data:
Original: Rewritten for process 1: Rewritten for process 2:
0x1010: movl 0x8(%rbp), %rdi 0x1010: movl 0x8(%rbp), %rdi 0x3010: movl 0x8(%rbp), %rdi
0x1013: addl $0x3, %rdi 0x1013: addl $0x3, %rdi 0x3013: addl $0x3, %rdi
0x1019: movl %rdi, 0x8(%rbp) 0x1019: movl %rdi, 0x8(%rbp) 0x3019: movl %rdi, 0x8(%rbp)
Lecture Note
Only code addresses, globals, and statics are rewritten. Stack-relative addresses (using
%rbp) don’t need rewriting because they are already relative to the base pointer.
Static Relocation Disadvantages
- No protection
- Process can destroy OS or other processes
- No privacy
- Cannot move address space after it has been placed
- May not be able to allocate new process
Lecture Note
A process could write
int x = 700; memset(x, 0, 1000)and overwrite memory belonging to another process. There is no hardware enforcement.
Approach 3: Dynamic Relocation (Base Register)
Goal: Protect processes from one another.
Requires hardware support:
- Memory Management Unit (MMU)
MMU dynamically changes process address at every memory reference:
- Process generates logical (or virtual) addresses (in their address space)
- Memory hardware uses physical (or real) addresses
graph LR
A[CPU] -->|logical address| B[MMU]
B -->|physical address| C[Memory]
Virtual Addresses
- Virtual addresses are independent of location in physical memory (RAM) that referenced data lives
- OS determines location in physical memory
- Instructions issued by CPU reference virtual addresses
- e.g., pointers, arguments to load/store instruction, PC
- Virtual addresses are translated by hardware into physical addresses (with some help from OS)
Hardware Support for Dynamic Relocation
Privileged (protected, kernel) mode: OS runs
- When enter OS (trap, system calls, interrupts, exceptions)
- Allows certain instructions to be executed (can manipulate contents of MMU)
- Allows OS to access all of physical memory
User mode: User processes run
- Perform translation of logical address to physical address
Base Register Implementation
Translation on every memory access of user process:
physical address = logical address + base register
- MMU checks mode — if kernel mode, address passes through unchanged
- If user mode, MMU adds base register to logical address to form physical address
- Each process has a different value in base register
- Dynamic relocation by changing value of base register
Example
P1 at physical 1KB–2KB (base = 1024), P2 at physical 4KB–5KB (base = 4096):
Virtual Physical P1: load 100, R1 100 load 1124, R1 P2: load 100, R1 100 load 4196, R1 P2: load 1000, R1 1000 load 5096, R1 P1: load 100, R1 100 load 2024, R1
Base Register Protection Problem
With only a base register, a process can still access memory beyond its allocation:
Lecture Note
P1 (base = 1024) could issue
store 3072, R1→ physical address = 3072 + 1024 = 4096, which falls in P2’s region. Base register alone does not provide protection.
Approach 4: Base+Bounds
Idea: Limit the address space with a bounds register.
- Base register: Smallest physical address (starting location)
- Bounds register: Size of this process’s virtual address space
- Sometimes defined as largest physical address (base + size)
- OS kills process if process loads/stores beyond bounds
Base+Bounds Implementation
Translation on every memory access of user process:
- MMU checks mode — if kernel mode, address passes through
- If user mode, MMU compares logical address to bounds register
- If logical address >= bounds → generate error (segfault)
- If logical address < bounds → physical address = logical address + base
Example
P1 (base = 1024, bounds = 1024), P2 (base = 4096, bounds = 1024):
P1 issues
store 3072, R1→ 3072 > 1024 (bounds) → interrupt OS! Protection enforced.
Managing Processes with Base and Bounds
Context-switch: Add base and bounds registers to PCB.
Steps:
- Change to privileged mode
- Save base and bounds registers of old process
- Load base and bounds registers of new process
- Change to user mode and jump to new process
Protection requirement:
- User process cannot change base and bounds registers
- User process cannot change to privileged mode
Base and Bounds Advantages
- Provides protection (both read and write) across address spaces
- Supports dynamic relocation
- Can place process at different locations initially and also move address spaces
- Simple, inexpensive implementation: few registers, little logic in MMU
- Fast: add and compare in parallel
Base and Bounds Disadvantages
- Each process must be allocated contiguously in physical memory
- Must allocate memory that may not be used by process (free space between heap and stack is wasted)
- No partial sharing: Cannot share parts of address space
Lecture Note
The entire region from code through heap free space through stack must be allocated contiguously, even if the process only uses a small fraction of it.
Approach 5: Segmentation
Divide address space into logical segments:
- Each segment corresponds to a logical entity in address space (code, stack, heap)
- Each segment has a separate base + bounds register
Segmented Addressing
Process now specifies segment and offset within segment.
How does a process designate a particular segment?
- Use part of logical address
- Top bits of logical address select segment
- Low bits of logical address select offset within segment
- If small address space, not enough bits:
- Implicitly by type of memory reference
- Special registers
Segmentation Implementation
MMU contains a Segment Table (per process):
- Each segment has its own base, bounds, and protection bits
Example
14-bit logical address, 4 segments → 2 bits for segment, 12 bits for offset (1 hex digit = 4 bits)
Segment Base Bounds R W 0 0x2000 0x6ff 1 0 1 0x0000 0x4ff 1 1 2 0x3000 0xfff 1 1 3 0x0000 0x000 0 0
Segmentation Translation Example
Segment numbers: 0 = code+data, 1 = heap, 2 = stack
Example
Given heap (seg 1) at physical 0x400, stack (seg 2) at physical 0x1600:
Virtual (hex) Translation Physical load 0x2010, R1seg 2, offset 0x010 → 0x1600 + 0x010 0x1610load 0x1010, R1seg 1, offset 0x010 → 0x400 + 0x010 0x0410load 0x1100, R1seg 1, offset 0x100 → 0x400 + 0x100 0x0500
How Do Stacks Grow?
Stacks grow downward, but the segment offset increases upward. Solution: add a “grows down” bit to the segment descriptor.
Example
Stack segment base = 28K, size = 4K (stack goes 16K → 12K virtual, 28K → 24K physical).
Virtual address
0x3C00= 15K → top 2 bits (0x3) = segment reference, offset = 0xC00 = 3K.Negative offset = subtract segment size from offset: 3K - 4K = -1K from top.
Add to base: 28K - 1K = 27K → physical address
0x6C00.
Segmentation in x86
| Register | Purpose |
|---|---|
| SS (Stack Segment) | Pointer to the stack |
| CS (Code Segment) | Pointer to the code |
| DS (Data Segment) | Pointer to the data |
| ES (Extra Segment) | Pointer to extra data |
| FS (F Segment) | Pointer to more extra data |
| GS (G Segment) | Pointer to still more extra data |
Advantages of Segmentation
- Enables sparse allocation of address space
- Stack and heap can grow independently
- Heap: if no data on free list, dynamic memory allocator requests more from OS (e.g., UNIX:
malloccallssbrk()) - Stack: OS recognizes reference outside legal segment, extends stack implicitly
- Different protection for different segments
- Enables sharing of selected segments (e.g., read-only code)
- Supports dynamic relocation of each segment
Disadvantages of Segmentation
- Each segment must be allocated contiguously in physical memory
- May not have sufficient physical memory for large segments
- External fragmentation — free memory is broken into non-contiguous chunks
Summary
- Operating systems virtualize memory to support multiprogramming
- Each process gets its own address space
- Processes are prevented from accessing each other’s memory
- Hardware support required for efficient execution
| Approach | Protection | Relocation | Sharing | Fragmentation |
|---|---|---|---|---|
| Time sharing | N/A | N/A | No | N/A |
| Static relocation | No | No (fixed at load) | No | No |
| Base register | No | Yes | No | Internal |
| Base+Bounds | Yes | Yes | No | Internal |
| Segmentation | Yes (per-segment) | Yes | Partial | External |
Related
- Navigation: ← Lecture 4 · Lecture 6 →
- Exam: Midterm 1
- Course: CS 537 Map of Content