Course 3 - Graduate Operating Systems
CS 6200 · Difficulty: 5/5
Brief Overview
This course focuses on understanding how operating systems are able to abstract and arbitrate the use of a computer system. In general it sits between managing user applications, hardware, and managing systems despite the complexity and diversity of hardware components.
In addition the course dives into three major projects that teach concepts on the following ideas:
- Develop a multi-threaded client server communication protocol from scratch using sockets, TCP protocol, and the ability to send and receive any file type.
- Create two processes which are able to pass information across shared memory space using inter process communication
- Using gRPC develop a multi-threaded client server able to pass files using HTTP2 and a .proto file.
Detailed Overview
Project Overview
Class was broken into two parts: Lectures and Projects.
- Lectures begin by focusing on a large overview of the purpose of an operating system...
- Projects focus on building a multi-threaded client server protocol to transfer files...
Project 1: Multi-Threaded GETFILE Protocol
This project involved implementing a custom file transfer protocol and building a multi-threaded client-server system in C. The main technologies used were C programming, pthreads, TCP sockets, mutex/condition variables, file I/O, protocol design.
Multi-threaded Server
- Boss thread: Continues listening and accepting new clients.
- Worker threads: Handles the actual serving operations.
- Connections are handed off to workers for processing.
Multi-threaded Client
- Boss thread: Enqueues file download requests into work queue.
- Worker threads: Process download requests concurrently.
Difficulty: 5/5
Summary: If you're new to C Programming this will be quite difficult. It's a great project and will teach a lot through trial and error.
Project 3: Inter Process Communication (No Project 2)
This project builds on Project 1 by converting the getfile server into a proxy server with caching capabilities using IPC mechanisms. The main technologies used were C programming, libcurl, POSIX shared memory, POSIX message queues, semaphores, and process synchronization.
Part 1: Proxy Server with libcurl
Goal of part 1 is to convert the getfile server into a proxy server that translates GETFILE requests into HTTP requests for remote servers.
- Replaces disk retrieval with web-based retrieval using libcurl's "easy" C interface.
- Implement
handle_with_curl()callback that maps HTTP response codes to GETFILE statuses (e.g. 404 toGF_FILE_NOT_FOUND). - Server reuses the boss-worker multi-threaded framework from Project 1.
Part 2: Cache Daemon with Shared Memory IPC
Goal of part 2 is to implement a separate cache process that communicates with the proxy across process boundaries, avoiding network requests for cached files.
- Two IPC channels with distinct roles: a command channel built on POSIX message queues for the proxy to send file requests to the cache, and a data channel built on POSIX shared memory segments for transferring file contents back.
- A pool of shared memory segments is created at startup and managed by the proxy; each request checks a segment out of the pool and returns it when the transfer completes.
- Synchronization between reader and writer on each segment is handled with semaphores, alternating turns so the cache writes a chunk, the proxy reads it, and the segment is reused until the full file is transferred.
- File length must be communicated through the channel since transfers are chunked and the proxy cannot know the size in advance.
- Proper cleanup of IPC resources on shutdown (signal handling), since orphaned shared memory and message queues persist after the process dies.
Key concepts in this project were designing an IPC protocol from scratch, semaphore-based producer-consumer synchronization across processes rather than threads, and debugging race conditions and deadlocks that only appear under concurrent load. Unlike Project 1, a mistake here can leave stale IPC resources on the system requiring manual cleanup.
Difficulty: 5/5
Summary: The hardest project of the course for me. Debugging semaphore state across two separate processes is a completely different experience from debugging threads, and tools like AddressSanitizer only get you so far. Getting the segment pool and cleanup logic right took significant trial and error, but it's the project where synchronization concepts finally clicked.
Project 4: gRPC Distributed File System
The final project moves from IPC on a single machine to a distributed file system (DFS) across a network, in the style of AFS. The main technologies used were C++, gRPC, Protocol Buffers, HTTP/2, inotify, and multi-threaded asynchronous services.
Part 1: gRPC Service Basics
Goal of part 1 is to build the RPC layer for basic file operations between client and server.
- Define the service and message types in a
.protofile and generate client/server stubs with the protobuf compiler. - Implement RPCs for store, fetch, delete, list, and file status (attributes such as size and modified time).
- Files are transferred as streamed chunks rather than single messages, using gRPC's streaming APIs in both directions.
- Handle deadlines/timeouts on every call and map gRPC status codes
(e.g.
DEADLINE_EXCEEDED,NOT_FOUND) to client behavior.
Part 2: Synchronized Cache Consistency
Goal of part 2 is to keep multiple clients' local caches consistent with the server using a weakly consistent model similar to AFS.
- Clients watch their local mount directory with inotify and automatically push changes to the server.
- A write-lock RPC ensures only one client can write a given file at a time; other clients must wait or fail gracefully.
- An asynchronous server-to-client notification channel broadcasts file list changes so clients can sync new, modified, or deleted files.
- CRC checksums determine whether a local file and server file actually differ before transferring, and modified times decide sync direction.
- Careful synchronization between the inotify watcher thread and the async gRPC listener thread to avoid feedback loops (syncing a file triggering the watcher, which triggers another sync).
Key concepts in this project were RPC-based distributed system design, cache consistency models, streaming data over HTTP/2, and coordinating concurrency both within the client (threads) and across the system (distributed locks).
Difficulty: 4/5
Summary: More conceptually interesting than brutally difficult — gRPC handles the low-level networking that Project 1 made you build by hand, so the challenge shifts to the consistency logic. The feedback loop between inotify events and sync operations is the classic gotcha. A satisfying capstone that ties together threading, networking, and protocol design from the whole course.