Being able to easily run and debug a simple operating system can be really useful when you want to learn how low level components are implemented. Xv6 is a very simple Unix-like operating system that allows you to do just that.

sillysaurus2 exemplified this in the Hacker News’ thread on Xv6:

Have you ever:

• Wondered how a filesystem can survive a power outage?
• Wondered how to organize C code?
• Wondered how memory allocation works?
• Wondered how memory paging works?
• Wondered about the difference between a kernel function and a userspace function?
• Wondered how a shell works? (How it parses your commands, or how to write your own, etc)
• Wondered how a mutex can be implemented? Or how to have multiple threads executing safely?
• How multiple processes are scheduled by the OS? Priority, etc?
• How permissions are enforced by the OS? Security model? Why Unix won while Multics didn’t (simplicity)?
• How piping works? Stdin/stdout and how to compose them together to build complicated systems without drowning in complexity?
• So much more!

I credit studying xv6 as being one of the most important decisions I’ve made; up there with learning vim or emacs, or touch typing. This is foundational knowledge which will serve you the rest of your life in a thousand ways, both subtle and overt. Spend a weekend or two dissecting xv6 and you’ll love yourself for it later. (Be sure to study the book, not just the source code. It’s freely available online. The source code is also distributed as a PDF, which seems strange till you start reading the book. Both PDFs are meant to be read simultaneously, rather than each alone.)

You can download Xv6’s source code, compile, and run under QEMU using the following commands:

It looks like this:

There are very few commands available but you can see UNIX here:

## Remote Debug Xv6 Under QEMU using GDB

Xv6’s Makefile has a rule to make this very easy (qemu-gdb):

Execution stopped before the first instruction and is now waiting for GDB to connect and supervise the execution (breakpoint, continue…). Open GDB from another shell:

Now you can set breakpoints, resume execution, and do whatever you want. Here I set a breakpoint at the exec function:

You can see that /init was the first process, it spawned sh and I entered ls in the console myself. Things are magically simple!