Pintos Operating System Kernel & Memory Allocation
Project Overview #
Pintos is an instructional operating system framework for x86 architectures. For this project, I built out kernel subsystems completely from scratch in C. The goal was to take a bare-bones shell and turn it into a fully functional operating system that could actually manage memory, handle complex thread synchronization, execute programs, and run a file system.
Thread Scheduling & Synchronization #
A reliable OS needs a smart way to manage threads, so I overhauled the basic scheduler to make it efficient and ensure it respected strict priority rules.
- Efficient Alarm Clock: The default setup had sleeping threads constantly checking the time (busy waiting), which burned through CPU cycles. I replaced this with an interrupt driven sleep queue. Threads now actually block and go to sleep until their timer expires, freeing up the CPU for other tasks.
- Priority Inheritance: One of the annoying problems in OS design is priority inversion. When a low priority thread holds a lock that a high priority thread desperately needs. To fix this, I built a strict priority donation protocol. A lock holder now temporarily inherits the priority of the highest priority thread waiting on that lock. It handles nested donations and revokes the priority the second the lock is released so the system doesn’t stall out.
Virtual Memory & Custom Allocation #
When you are writing a bare-metal kernel, you don’t get the luxury of standard C libraries—there is no built in malloc or free. I had to engineer my own memory management subsystems to handle both kernel level memory and user space virtual memory.
- Custom Kernel Allocator (
malloc): I built a block allocator from scratch. It segments raw memory pages into distinct blocks, maintains free lists, and uses an efficient allocation algorithm to serve requests of any byte size while keeping memory fragmentation to an absolute minimum. - Demand Paging & Swapping: I implemented a supplemental page table to handle page faults lazily. Instead of loading everything into memory at startup, pages are only pulled from the executable or swap space when a program explicitly tries to access them.
- Page Replacement: Because physical memory runs out quickly, I designed an eviction algorithm (using an LRU/clock approximation) to intelligently write older pages back to a swap partition when the system hits its limits.
/*kernel memory allocation */
void *malloc (size_t size) {
if (size == 0) return NULL;
//Determine the optimal block size arena for the request
struct arena *a = find_best_arena(size);
//Acquire a block from the free list, handling page alloc if the arena is exhausted
void *block = arena_alloc(a);
return block;
}