Introduction
The best way to solidify your understanding of operating systems is to build OS components yourself. These capstone projects will challenge you to apply concepts from the entire series.
Apply your knowledge by building practical OS components—a Unix shell, thread pool, paging simulator, and more.
The best way to solidify your understanding of operating systems is to build OS components yourself. These capstone projects will challenge you to apply concepts from the entire series.
Build a simple command-line shell supporting commands, pipes, and I/O redirection.
// Mini Shell - Core structure
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_LINE 1024
#define MAX_ARGS 64
void execute_command(char **args) {
pid_t pid = fork();
if (pid == 0) {
// Child process
execvp(args[0], args);
perror("execvp failed");
exit(1);
} else if (pid > 0) {
// Parent waits
int status;
waitpid(pid, &status, 0);
} else {
perror("fork failed");
}
}
int main() {
char line[MAX_LINE];
char *args[MAX_ARGS];
while (1) {
printf("mysh> ");
if (!fgets(line, MAX_LINE, stdin)) break;
// Parse input into args array...
// Handle built-ins (cd, exit)...
// Handle pipes and redirection...
execute_command(args);
}
return 0;
}
Implement a reusable thread pool for efficient task execution.
// Thread Pool - Core structure
#include <pthread.h>
#include <stdlib.h>
typedef struct {
void (*function)(void *);
void *argument;
} task_t;
typedef struct {
pthread_t *threads;
task_t *task_queue;
int queue_size;
int queue_front, queue_rear, queue_count;
pthread_mutex_t lock;
pthread_cond_t not_empty;
pthread_cond_t not_full;
int shutdown;
} threadpool_t;
void *worker_thread(void *arg) {
threadpool_t *pool = (threadpool_t *)arg;
while (1) {
pthread_mutex_lock(&pool->lock);
while (pool->queue_count == 0 && !pool->shutdown) {
pthread_cond_wait(&pool->not_empty, &pool->lock);
}
if (pool->shutdown) {
pthread_mutex_unlock(&pool->lock);
break;
}
// Dequeue task and execute
task_t task = pool->task_queue[pool->queue_front];
pool->queue_front = (pool->queue_front + 1) % pool->queue_size;
pool->queue_count--;
pthread_cond_signal(&pool->not_full);
pthread_mutex_unlock(&pool->lock);
(task.function)(task.argument);
}
return NULL;
}
Simulate virtual memory with page tables and replacement algorithms.
// Paging Simulator - Core structure
#include <stdio.h>
#include <stdlib.h>
#define NUM_PAGES 256
#define NUM_FRAMES 64
typedef struct {
int valid;
int frame_number;
int referenced;
int modified;
} page_table_entry_t;
typedef struct {
int page_number;
int loaded;
} frame_t;
page_table_entry_t page_table[NUM_PAGES];
frame_t physical_memory[NUM_FRAMES];
int page_faults = 0;
int clock_hand = 0; // For CLOCK algorithm
int clock_replace() {
while (1) {
if (!page_table[physical_memory[clock_hand].page_number].referenced) {
int victim = clock_hand;
clock_hand = (clock_hand + 1) % NUM_FRAMES;
return victim;
}
page_table[physical_memory[clock_hand].page_number].referenced = 0;
clock_hand = (clock_hand + 1) % NUM_FRAMES;
}
}
void access_page(int page_number) {
if (!page_table[page_number].valid) {
page_faults++;
int frame = clock_replace();
// Evict old page, load new page...
page_table[page_number].valid = 1;
page_table[page_number].frame_number = frame;
}
page_table[page_number].referenced = 1;
}
Simulate different scheduling algorithms and compare their performance.
// Scheduler Simulator
typedef struct {
int pid;
int arrival_time;
int burst_time;
int remaining_time;
int completion_time;
int waiting_time;
int turnaround_time;
} process_t;
void round_robin(process_t *processes, int n, int quantum) {
int time = 0;
int completed = 0;
int *queue = malloc(n * sizeof(int));
int front = 0, rear = 0;
// Add processes as they arrive
// Execute for quantum or until burst complete
// Track waiting time and turnaround time
// Calculate average metrics
printf("Avg Waiting Time: %.2f\n", avg_waiting);
printf("Avg Turnaround Time: %.2f\n", avg_turnaround);
}
Implement a basic file system with inodes, directories, and data blocks.
// Simple File System structures
#define BLOCK_SIZE 4096
#define NUM_BLOCKS 1024
#define NUM_INODES 128
typedef struct {
int type; // 0=free, 1=file, 2=directory
int size;
int blocks[12]; // Direct blocks
int indirect; // Indirect block
char name[256];
} inode_t;
typedef struct {
char data[BLOCK_SIZE];
} block_t;
inode_t inodes[NUM_INODES];
block_t disk[NUM_BLOCKS];
int free_block_bitmap[NUM_BLOCKS];
int fs_create(const char *name);
int fs_write(int fd, const void *buf, int size);
int fs_read(int fd, void *buf, int size);
int fs_delete(const char *name);
Build your own malloc() implementation with different allocation strategies.
// Custom Memory Allocator
typedef struct block_header {
size_t size;
int free;
struct block_header *next;
} block_header_t;
static block_header_t *free_list = NULL;
static void *heap_start = NULL;
void *my_malloc(size_t size) {
// First-fit: find first block that fits
block_header_t *curr = free_list;
while (curr) {
if (curr->free && curr->size >= size) {
curr->free = 0;
// Optionally split block if much larger
return (void *)(curr + 1);
}
curr = curr->next;
}
// No suitable block, extend heap with sbrk()
return extend_heap(size);
}
void my_free(void *ptr) {
if (!ptr) return;
block_header_t *header = (block_header_t *)ptr - 1;
header->free = 1;
// Coalesce with adjacent free blocks
}
Challenge Extensions:
══════════════════════════════════════════════════════════════
Shell:
• Tab completion
• Signal handling (Ctrl+C, Ctrl+Z)
• Shell scripting support
Thread Pool:
• Dynamic resizing
• Task priorities
• Future/Promise pattern
Paging:
• Working set model
• Multi-level page tables
• TLB simulation
File System:
• Journaling
• Symbolic links
• FUSE integration (real mountable FS)
Congratulations on completing the Computer Architecture & Operating Systems Mastery series! You've journeyed from digital logic gates to kernel internals.