wisconsin
Lecture 5 - Virtualization: CPU to Memory

Lecture 5 - Virtualization: CPU to Memory

Curated by Last modified: 10 min read

Instructor: Michael Swift Topics: CPU virtualization recap, memory virtualization, address spaces, static relocation, dynamic relocation, base and bounds, segmentation


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
SegmentContentsGrowth Direction
Code (top, 0KB–1KB)Instructions (the code segment)Fixed
Heap (below code)malloc()’d data, dynamic data structuresGrows downward
Stack (bottom, 15KB–16KB)Local variables, arguments, return valuesGrows 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 main is at the base. When foo is called, its frame (containing local A and parameter Z) is pushed on top. The printf arguments 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)
  • %rbp is the base pointer — points to base of current stack frame
  • %rip is the instruction pointer (program counter)

Example

With initial %rip = 0x10, %rbp = 0x200:

  1. Fetch instruction at addr 0x10 → Exec: load from addr 0x1f8 (0x200 - 0x8)
  2. Fetch instruction at addr 0x13 → Exec: no memory access (register add)
  3. Fetch instruction at addr 0x19 → Exec: store to addr 0x1f8

Memory Practice

int x;
int main(int argc, char *argv[]) {
    int y;
    int* z = malloc(sizeof(int));
}
AddressLocation
xStatic data/code
mainStatic data/code
yStack
zStack
*zHeap

How to Virtualize Memory

Problem: How to run multiple processes simultaneously when addresses are “hardcoded” into process binaries?

Possible solutions:

  1. Time sharing
  2. Static relocation
  3. Base (dynamic relocation)
  4. Base+Bounds
  5. 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):

VirtualPhysical
P1: load 100, R1100load 1124, R1
P2: load 100, R1100load 4196, R1
P2: load 1000, R11000load 5096, R1
P1: load 100, R1100load 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:

  1. MMU checks mode — if kernel mode, address passes through
  2. If user mode, MMU compares logical address to bounds register
  3. If logical address >= bounds → generate error (segfault)
  4. 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:

  1. Change to privileged mode
  2. Save base and bounds registers of old process
  3. Load base and bounds registers of new process
  4. 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)

SegmentBaseBoundsRW
00x20000x6ff10
10x00000x4ff11
20x30000xfff11
30x00000x00000

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)TranslationPhysical
load 0x2010, R1seg 2, offset 0x010 → 0x1600 + 0x0100x1610
load 0x1010, R1seg 1, offset 0x010 → 0x400 + 0x0100x0410
load 0x1100, R1seg 1, offset 0x100 → 0x400 + 0x1000x0500

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

RegisterPurpose
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: malloc calls sbrk())
  • 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
ApproachProtectionRelocationSharingFragmentation
Time sharingN/AN/ANoN/A
Static relocationNoNo (fixed at load)NoNo
Base registerNoYesNoInternal
Base+BoundsYesYesNoInternal
SegmentationYes (per-segment)YesPartialExternal

Linked to this page