Jul 14 2021 Last edited Jul 29 2021

Let's write an OS

Before getting started...

Let's get one thing straight first: writing an OS (even a tiny and useless one as in this case) is a daunting, arduous and sizable endeavor. This fact is demonstrated by the number of like projects that stagnate half way through their course to never (not yet, at least) reach their conclusion.

Do not be surprised if this project suffers the same fate. I am far from being an expert in the area of OS development (best approximated by the distance between the Earth and the nearest end of the Universe) and my primary aim in beginning this project is only to expand my own knowledge. If someone comes along and picks up something instructional or useful, I'll be happy. But if it all just crashes and burns, I'll be happy too.

This article will be written incrementally so don't panic if the text just drops off; there is no extensive plan for this project and we will mostly be flying by the seat of our pants. If you want to contribute to the project, download any of the code, fix any typos or errata, or just read the text in Mardown, visit the github repo.

Finally, in no particular order, I'd like to suggest similar projects that have greatly influenced and inspired this one:

An additional mention is owed to the tremendous book Operating Systems: Three Easy Pieces (Remzi H. Arpaci-Dusseau and Andrea C. Arpaci-Dusseau) . Take a look, it's free ;).

Background info

My original intention for this section was to provide a small history of operating systems to present some context for the task ahead; while attempting to do so however, one thing became quickly apparent: I'm a 21 y/o with zero experience in OS development and only a beginner's understanding of how they work: wth could I say on the history of operating systems? I wasn't around when programs were literally circuits that were physically connected to computers and I've never touched a punch card; the first computer I used ran Windows 7. That being said, I may return to write something of a history here after progressing further into this project if I feel like I have something insightful to say, but, as it stands right now, I'd feel much more comfortable leaving the subject of operating system history to more capable sources (the wikipedia page is a good jumping off point).

There is, however, plenty of background information we should still cover first before we can even attempt to write something bootable, the most pressing of which being: what is an operating system?

Now, if you've ever owned a computer, you're likely to have an idea of what an operating system is; you've probably heard of Windows, MacOS or Linux. But if you had to define what an operating system does, what its purpose and responsibilities are, what would that definition look like? Turns out, there isn't one supreme, all-encompassing definition that fits every OS; there are a lot of different operating systems out there, and each one was designed to solve a different set of problems. Nevertheless, there are problems that every operating system needs to solve and this is exactly how we'll construct our initial defintion:

An operating system is a piece of software whose main responsibility is managing the computer's hardware (i.e. operating the system) and providing an environment and an interface for userland programs.

Let's take a quick stroll through this definition. An operating system operates the system: the most underlying responsibility of an OS is to manage the computer's hardware efficiently and effectively. Similar to an orchestra, a computer is made up of many differentiated components that need to work together in order to make the system fucntion productively as a whole. Extending this metaphor, a conductor is needed to direct and delegate such a performance: this is where our operating system fits into the orchestra.

That brings us to the second part of the definition: an operating system should provide an environment and interface for userland programs. One important principle of computer science is that of modularity: breaking a problem or task into small, modular and independent pieces that fit together to form an overall solution. Modularity simplifies problem-solving and makes software easier to manage, repair and scale. Imagine if every program you wrote also needed to worry about the internal mechanics and complexity of writing to an external device such as an SSD or a display; a better way to solve this problem is to have one program solve the problem of writing to such a device and then simply pass what you want written to that program (of course, this is not exactly how it works, but you get the idea). This is what we mean by providing an interface. By environment, we mean the ability to isolate running programs from one another for the purposes of security, manageability and simplicity for the user application programmer's sake.

For right now, we're keeping all of this simple: there's no reason to cross any of these bridges yet. But trust me, there will be many bridges to cross. We are trying to write an OS after all.

So, at this point we have a better idea of what an operating system is and what we're getting ourselves into. Let's talk game plan then for what comes next. In the next section, we should aim to write something that can boot; that's it. It should do literally nothing but boot. We'll talk all about how we'll achieve that in the next section, but first a word on what you'll need before moving forward.

  1. A good handle on the C programming language and x86 Assembly
  2. We will be writing our OS principally in C and x86 Assembly, so being competent with those technologies is an obvious prerequisite.

  3. Qemu, NASM, and GCC
  4. We will be using Qemu to emulate an x86 environment, and GCC and NASM to compile and assemble our code. Look up how to install and set up these tools on your computer beforehand.

With that out of the way, let's jump in.

Chapter 1: A minimal boot