OMSCS Course Review: CS 6200 Graduate Introduction to Operating Systems
By Anthony Mattas

I took an operating systems class in college and remembered the basics like processes, threads, virtual memory, and scheduling. CS 6200 went over these topics again, but in much more detail. After years of working with real systems, the material meant more to me this time.
This course isn't an easy starting point. If you want to get back into programming gently, you might want to pick a different class. But if you want a tough refresher that connects theory to real-world work, this is a good option.
Operating Systems
An operating system has three main jobs: abstraction, which hides hardware complexity; arbitration, which decides who gets resources; and isolation, which keeps programs from interfering with each other. I already knew these ideas, but CS 6200 went deeper than my undergrad class. We studied the trade-offs in different designs, read important research papers, and built more complex systems that forced us to work through the details.
One design principle from the course that really stayed with me is to keep mechanisms and policies separate. The operating system creates flexible tools, like a scheduler, that can use different rules, such as round-robin or priority-based scheduling. The same OS kernel can act very differently depending on its setup.
Processes and Threads
Creating a new process, with all its memory and page tables, uses a lot of resources. That's why threads are helpful. We learned about threads in undergrad, but this course went into much more detail about different threading models.
Threads are different from processes because they share the same address space as their parent. They just need their own stack and registers, so they're faster to create and easier to switch between.
I learned something new: threads are still useful even when you have more threads than CPUs. If a thread waits on I/O longer than it takes to switch threads, it's worth switching. This changed how I think about writing concurrent code.
The course also covers different threading models: one-to-one, many-to-one, and many-to-many, each with its own pros and cons. One-to-one gives full kernel support but makes system calls expensive. Many-to-one is portable, but one blocking call can stop everything. Many-to-many tries to balance both but is more complex.
Concurrency
When several threads access shared data without coordination, things can go wrong. I learned this in undergrad and have debugged many race conditions. This course made me go back to the basics of concurrency tools, instead of just using them out of habit.
Mutexes let only one thread use a resource at a time, while others wait. The protected part of the code should be short, or you lose the benefits of parallelism, which many developers forget. Condition variables let threads sleep until something changes, so they don't waste CPU cycles. Spinlocks are best when you expect a short wait, shorter than blocking would take.
In college, every computer science student spends a lot of time dealing with deadlocks when learning about concurrency. Preventing deadlocks is actually simple: always acquire locks in the same order. If every thread grabs mutex A before mutex B, you won't get a cycle.
Virtual Memory
Each process thinks it has its own continuous address space, but that's not really the case. The operating system and the memory management unit map virtual addresses to the available physical memory.
The page fault handler is where things get interesting. If a process tries to access memory that isn't there, the handler fetches the needed pages from disk or allocates new memory, and the process doesn't notice. The course covers page table structures, TLB caching, and the pros and cons of different page sizes.
The Assignments
Most assignments were done in C and were well designed. They included building a simple web server with sockets, making an app that uses interprocess communication, and creating a distributed file system with gRPC.
The assignments didn't always line up with the lecture topics. Sometimes, you had to learn extra material on your own. This could be because of the shorter summer semester, but be prepared to do some self-study to fill in the gaps.
Since the assignments use a low-level language, make sure to give yourself enough time. C is easy for me because it was my first real language, but I saw many classmates struggle with it. Even as an experienced C programmer, debugging took longer than I expected. Memory bugs are harder to find than exceptions in higher-level languages. You'll end up using Valgrind and GDB, like it or not.
What Stuck
Overall, the lectures are clear, and the projects build on each other in a logical way. Ideas from the first project appear again in the third. While I did not need to interact much with the professor during this course, Dr. Ada Gavrilovska's videos were very engaging. She explained complex concepts using real-world examples, like comparing CPU scheduling to a toy factory. This skill, which is rare in online learning, made the content interesting, understandable, and engaging.
After this class, a few key lessons stayed with me:
- Optimize for the common case. Know your workload before picking abstractions.
- Mechanisms vs. policies. To build flexible systems, make behavior configurable.
- Cache locality matters more than you think. Sometimes the "slower" algorithm wins because it plays nice with the memory hierarchy.
- Synchronization is hard. Use the simplest primitive that works.
Should You Take It?
If you're comfortable with C and don't mind low-level debugging, then yes, you should take it. Even if you took OS in undergrad, the extra depth and the projects make it worth your time. It's one of the heavier workload courses in OMSCS, but it filled in gaps I didn't know I had and gave me a much better understanding of the systems I use every day.
Category: OMSCS
Comments
Sign in to join the conversation
No comments yet. Be the first to share your thoughts!