Booting Linux in Qemu and Writing Pid 1 in Go to Illustrate Kernel as Program
Key topics
Diving into the intricacies of the Linux kernel, a recent blog post sparked a lively discussion on rethinking the kernel as just another program, with many commenters sharing their insights on the topic. Some, like ronsor and tosti, geeked out over stripping down the kernel to its bare essentials, while others, such as bitwize, recommended exploring alternative BSD kernels for a deeper understanding. As the conversation unfolded, perspectives varied, with some, like geonineties, reframing the kernel as a library, and charcircuit likening it to a platform, echoing the post author's forthcoming series direction hinted at by zsoltkacsandi. The thread's engaging blend of technical exploration and conceptual debate makes it a fascinating read for anyone curious about the inner workings of operating systems.
Snapshot generated from the HN discussion
Discussion Activity
Very active discussionFirst comment
7d
Peak period
50
156-168h
Avg / period
22.8
Based on 91 loaded comments
Key moments
- 01Story posted
Dec 4, 2025 at 9:25 AM EST
29 days ago
Step 01 - 02First comment
Dec 11, 2025 at 1:52 AM EST
7d after posting
Step 02 - 03Peak activity
50 comments in 156-168h
Hottest window of the conversation
Step 03 - 04Latest activity
Dec 12, 2025 at 5:59 PM EST
21 days ago
Step 04
Generating AI Summary...
Analyzing up to 500 comments to identify key contributors and discussion patterns
Want the full context?
Jump to the original sources
Read the primary article or dive into the live Hacker News thread when you're ready.
I have had a bit of a dream of building a full desktop operating system around seL4 [1], with all drivers in user space and the guts fully verified in Isabelle, but learning about this level of code kind of feels like drinking from a firehose. I would like to port over something like xserver and XFCE and go from there, but I've never made a proper attempt because of how overwhelming it feels.
[1] I know about sculpt and Genode, and while those are interesting, not quite what I want.
Play a bit with user mode linux [1] the kernel becomes literally a linux program, that I believe you can even debug with gdb (hazy memory as I tried uml last time maybe a decade ago)
In theory you can also attach gdb to qemu running linux, but that's more complicated.
[1] https://en.wikipedia.org/wiki/User-mode_Linux
The fact that it's also a product/program is some brainfucky exercise that might either be an interesting hobby thought experiment OR it might be a very relevant nuance that will be useful to the top 0.1% of professionals who need a 99.9% accuracy, like the difference between classical and relativistic mechanics.
I mean, sure you are right that kernels are programs and that money is a product, and that gravity is not a force. But I am a mere mortal and I will stick to my incorrect and incomplete mental model at a small expense of accuracy to the great advantage of being comprehensible.
Completely agree with this framing. We will get there by the end of the series.
This is why Linux is excellent. Users of other operating systems often remind people to update their device drivers. A non-technical Linux responds asking what the heck device drivers are. To the casual user, device drivers become invisible because they work exactly as intended.
Not are drivers are like that. For instance, drivers/input/serio/ps2-gpio.c is all about timing the right signals.
The way I see it, everything is tied together as some kind of flow chart where different elements have different jobs. Linux is quite a small part of the system when compared to Google Chrome. Even if you were to invert the cake, as a whole it still wouldn't make sense to me to see it that way.
Hardware tends to have more distinct layering than the lalala-land of software where pretty much anything goes.
Sound blaster(16) also came close to being standard enough that games could just support that.
Extrapolating I think MS-DOS was on a nice trajectory to having complete enough (and reasonably simple and non-bloated!) APIs for everything important, when it was killed off. Late MS-DOS 32-bit games were usually trivial to install and run.
I kind of agree, but the kernel as a program serves a pedagogical framing here.
The goal of the post is to make it more tangible for developers, they write programs that are files on the disk, and you can interact with them. That's where the analogy came from.
the author's apparent epiphany is realizing that init is just a program. the kernel is, of course, software as well, but it does injustice to both "program" and "kernel" to lump them together.
https://docs.kernel.org/admin-guide/kernel-parameters.html
Talos is really an interesting linux distribution because it has no classical user space, i.e. there is no such thing as a $PATH including /bin, /usr/bin, etc. The shell is instead a network API, following the kubernetes configuration-as-code paradigm. The linux host (node) is supposed to run containerized applications. If you really want to, you can use a special container to get access to the actual user space from the node.
[1] https://www.talos.dev/ [2] https://github.com/siderolabs/talos/releases/tag/v1.11.5
Go seemed a perfect fit, it is easy to pick up the syntax and see what is going on, but you can still be close to the OS.
Thank you. I have always wondered that.
And updated domain: https://mustafaakin.dev/posts/2016-02-08-writing-my-own-init...
The only reason is because I like to be explicit, and I could not know what was set before in the user's environment.
My goal with this post and the whole (work in progress) series is to fill the gap between "here are the commands to do X" and "if you want to contribute to the kernel, you need to learn this" style books and tutorials.
I want something in between, for developers who just want a solid mental model of how Linux fits together.
The rough progression I have in mind is:
1. the Linux kernel as "just a program"
2. system calls as the kernel's API
3. files as resources manipulated through system calls, forming a consistent API
4. the filesystem hierarchy as a namespace system, not a direct map of disk layout
5. user/group IDs and permissions as the access control mechanism for resources (files)
6. processes, where all of the above comes together
I deliberately chose Go for the examples instead of C because I want this to be approachable to a broader audience of developers, while still being close enough to the OS to show what's really going on.
As a developer, this kind of understanding has been incredibly useful for me for writing better software, debugging complex issues with tools like strace and lsof, or the proc fs. I would like to help others to gain the same knowledge.
That said, this series will also give you practical, applicable knowledge as we progress.
https://raby.sh/sigterm-and-pid-1-why-does-a-container-linge...
I guess also one of the points of using Go was the fact it has own memory management and interacts only with the kernel.
I mean, had you used C, it would be better to compile it statically, otherwise you'd need to put also glibc and ld.so and what else into the initrd, I guess
One thing that could be improved is that the author could break down some of the commands, and explain what their arguments mean. For example:
> mknod rootfs/dev/console c 5 1
Depending on the reader's background, the args 'c', '5', and '1' can look arbitrary and not mean much. Of course, we can just look those up, and it doesn't make the article worse.
There is also "b" for block device (e.g. a disk, a partition, or even something like a loopback device) and "p" for FIFOs (similar to mkfifo).
The two numbers are just identifiers to specify the device, so in case of `5 1` it means the systems tty console, while `1 8` would mean "blocking random byte device" (mknod dev/random c 1 8)
1. /sbin/init
2. /etc/init
3. /bin/init
4. /bin/sh
It dropping you into a shell is a pretty neat little way to allow recovery if you somehow really borked your init
> Bailing out, you are on your own. Good luck.
https://unix.stackexchange.com/questions/96720
The kernel has a different error message: "No working init found. Try passing init= option to kernel."[2]
1: https://github.com/archlinux/mkinitcpio/blob/2dc9e12814aafcc... 2: https://github.com/torvalds/linux/blob/d358e5254674b70f34c84...
That is: this seemed like the first 3 minutes of the first lecture on an freshman OS course, or similar in any book on systems. The complication you refer to - is it just from the clutter of adjacent words (EFI, grub, kmod maybe?)
I agree, little nuggets like this are valuable even if know it already.
Purely academic.
Do anything necessary from there to boot your game, and record those steps in a script. When that's done you can just point your init cmdline to that script (doesn't even have to be a binary, a script with the #!/bin/bash shebang should just work).
Containers work similarly, except that they don't take the whole system down when their PID 1 exits. That's why containers often don't have a process manager inside, but Linux based operating systems do.
> When the kernel starts it does not have all of the parts loaded that are needed to access the disks in the computer, so it needs a filesystem loaded into the memory called initramfs (Initial RAM filesystem).
The kernel might not have all the parts needed to mount the filesystem, especially on a modern Linux distro that supports a wide variety of hardware.
Initramfs exists so that parts of the boot logic can be handled in userspace. Part of this includes deciding which device drivers to load as kernel modules, using something like udev.
Another part is deciding which root filesystem to mount. The root FS might be on an LVM volume that needs to be configured with device-mapper, or unlocked with decrypt. Or it might be mounted over a network, which in turn requires IP configuration and authentication. You don't want the kernel to have those mechanisms hard-coded, so initramfs allows handling them in userspace.
But strictly speaking, you don't need any of that for a minimal system. You can boot without initramfs at all, as long as no special userspace setup is required. i.e., the root FS is a plain old disk partition specified on the kernel command line, and the correct drivers (e.g. for a SCSI/SATA hard drive) are already linked into the kernel.
This was 20yrs ago. Gentoo was really a great teacher.
Then at some point the other week I realized I could technically have a working Linux "system" with nothing more than a kernel and a dirt simple hello world program in /sbin/init.
I haven't had the time or inclination to scratch that itch but it's nice to see this article confirm it.
- https://www.kernel.org/doc/html/v5.9/virt/uml/user_mode_linu...
maybe the audience is people who've never heard of init or thought about kernel vs userspace.
From https://news.ycombinator.com/item?id=41270425 re: "MiniBox, ultra small busybox without uncommon options":
> There's a pypi:SystemdUnitParser.
> docker-systemctl-replacement > systemctl3.py parses and schedules processes defined in systemd unit files: https://github.com/gdraheim/docker-systemctl-replacement/blo...
From a container2wasm issue about linux-wasm the other day: https://github.com/container2wasm/container2wasm/issues/550#... :
> [ uutils/uucore, uutils/coreutils, uutils/procps, uutils/util-linux, findutils, diffutils, toybox (C), rustybox, ]
https://gokrazy.org/
I’d love a similarly styled part two that dives into making a slightly useful distro from “scratch” in go.
https://docs.google.com/presentation/d/1rAAyOTCsB8GLbMgI0CAb...
https://www.youtube.com/watch?v=69Zy77O-BUM