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:
- The little book about OS development (Erik Helin, Adam Renberg)
- os-tutorial (Carlos Fenollasa)
- Writing an OS in Rust (Philipp Oppermann)
- intermezzOS (Steve Klabnik)
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.
- A good handle on the C programming language and x86 Assembly
- Qemu, NASM, and GCC
We will be writing our OS principally in C and x86 Assembly, so being competent with those technologies is an obvious prerequisite.
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.