In the Spring of my sophomore year at CMU (Spring 2012), I took a class called 15-410: Operating System Design and Implementation, usually referred to as "OS." The class is widely regarded as one of the more difficult courses in the CS curriculum, primarily because of its scope: the central project of the class is to write a full x86 kernel from the ground up in six weeks, complete with memory paging, processes, threads, keyboard and console drivers, and a whole host of system calls. While my grades in the class were far from perfect and the kernel that my partner and I turned in certainly had its share of bugs, OS was among my favorite classes during my time at CMU because I really got to understand how computer systems work, starting from the hardware. It was also a rather formative class: our kernel, at approximately 12000 lines of C and assembly, was the largest software project I had ever worked on, and the first where I was sure to meticulously and uniformly document every function and every member, even the "obvious" ones. Not only did OS teach me how to computer, OS taught me how to code.
But alas, six weeks is not much time, and I had other classes to attend to and extracurricular activities which also took some of my time. My partner and I worked as hard as we could and as best we could, but as a simple result of the time pressure, we made some design decisions based on "this seems to work" rather than "this is the right way to do it." Since that semester, I've always thought it would be fun to go back and revisit kernel development, and to either fix the bugs in our kernel or to just write a new one (with a self-defined specification, rather than the one assigned to us). However, only now, having graduated, do I actually have the time to do so.
And so, a few nights ago, I started writing a kernel.
Come Fly with Me
OS Development is hard, and I only have a few months before I enter the "real world" of gainful employment, and thus can't stay up until sunrise on my parents' couch, sipping wine and pondering the myriad intricacies of x86. I also plan on doing... well, something else this summer. I'm not going to spend my entire first (and likely last) unencumbered month-plus break in a decade writing code.
I do want to share this project, though, partly as self-reference and partly as a help for anyone out there who's crazy enough to want to take on a similar project. I can't guarantee how far we'll get, or how often I remember to post updates, but follow along on GitHub and we'll see what happens.
So what is a kernel, really? The kernel is the code that runs when the computer first starts, before anything shows up on the screen*, before any devices work, and before any programs run. The kernel's job is to manage system resources, including available memory, devices, and CPU time. The kernel's job also includes making sure that programs can run in complete** isolation from each other, and that one misbehaving process can't compromise or crash the whole system.
If you want to follow along with this series of articles (however far it gets), you should:
- Be very comfortable with C and concepts like memory management, pointer arithmetic, and bitwise operations.
- Be comfortable with assembly. I'm going to be using GAS syntax, but if you come from the world of Intel syntax, hopefully you'll be able to follow along.
- Understand x86 calling conventions, stack layouts, and at least a little about memory layouts.
- Know how to use a debugger. I might or might not talk much about debugging (it's usually (but not always) more fun to talk about what works than what doesn't), but if you're following along you will hit some nasty bugs.
- Like this stuff.
If you said yes to all of the above (or are willing to dive in a little over your head), let's venture forth into the exciting world of kernel development!
Update 2014-05-31: I forgot the footnotes:
* The BIOS, bootloader, and a few other things can write to the screen
before the kernel gets loaded.
** Complete isolation, except when processes ask to talk with each other.