惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
L
LangChain Blog
Jina AI
Jina AI
WordPress大学
WordPress大学
人人都是产品经理
人人都是产品经理
S
Secure Thoughts
T
The Exploit Database - CXSecurity.com
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 聂微东
小众软件
小众软件
Apple Machine Learning Research
Apple Machine Learning Research
C
Cyber Attacks, Cyber Crime and Cyber Security
Project Zero
Project Zero
T
Threat Research - Cisco Blogs
量子位
G
GRAHAM CLULEY
腾讯CDC
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
C
CERT Recently Published Vulnerability Notes
The Hacker News
The Hacker News
C
Cisco Blogs
Scott Helme
Scott Helme
Spread Privacy
Spread Privacy
宝玉的分享
宝玉的分享
V
V2EX
博客园 - 三生石上(FineUI控件)
T
Tor Project blog
P
Proofpoint News Feed
雷峰网
雷峰网
D
Darknet – Hacking Tools, Hacker News & Cyber Security
V
Vulnerabilities – Threatpost
PCI Perspectives
PCI Perspectives
博客园_首页
L
LINUX DO - 最新话题
IT之家
IT之家
有赞技术团队
有赞技术团队
博客园 - Franky
Hacker News: Ask HN
Hacker News: Ask HN
Last Week in AI
Last Week in AI
The Cloudflare Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
美团技术团队
博客园 - 【当耐特】
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Security Archives - TechRepublic
Security Archives - TechRepublic
L
LINUX DO - 热门话题
AWS News Blog
AWS News Blog
S
Security Affairs
T
Tailwind CSS Blog

Hacker News

Introducing Claude Opus 4.7 Qwen Studio The Future of Everything is Lies, I Guess: Where Do We Go From Here? GitHub - SeanFDZ/macmind: Single-layer transformer in HyperTalk for the classic Macintosh Show HN: Agent-cache – Multi-tier LLM/tool/session caching for Valkey and Redis Moving a large-scale metrics pipeline from StatsD to OpenTelemetry / Prometheus GitHub - Nightmare-Eclipse/RedSun: The Red Sun vulnerability repository GitHub - SethPyle376/hiraeth: Local AWS emulator focused on fast integration testing, with SQS support, SQLite-backed state, and a debug-friendly web UI. GitHub - macOS26/Agent: Any AI, replaces Claude Code, Cursor, OpenClaw. Over 18 LLM providers (Claude, OpenAI, Gemini, Ollama, Zai, HF, Qwen) wired into a native Mac app that writes code, builds Xcode projects, bumps versions, manages git, automates Safari, use AppleScript, JS or Accessibility, extend Agent! w/ MCP Servers, run tasks from your iPhone via Messages. YouTube now lets you turn off Shorts I Made a Terminal Pager Burgers | マクドナルド公式 Commands — HackerNews CLI documentation ChatGPT for Excel PiCore - Raspberry Pi Port of Tiny Core Linux Live Nation illegally monopolized ticketing market, jury finds Google Broke Its Promise to Me. Now ICE Has My Data. Founding Engineer at Adaptional | Y Combinator CRISPR takes important step toward silencing Down syndrome’s extra chromosome GitHub - saffron-health/libretto: The AI toolkit for building reliable browser automations US v. Heppner (S.D.N.Y. 2026) no attorney-client privilege for AI chats [pdf] Unexpected €54k billing spike in 13 hours: Firebase browser key without API restrictions used for Gemini requests Retrofitting JIT Compilers into C Interpreters IPv6 – Google The Accursèd Alphabetical Clock Cybersecurity Looks Like Proof of Work Now Fragments: April 14 Cal.com Goes Closed Source: Why AI Security Is Forcing Our Decision | Cal.com - Scheduling Software for Online Bookings Laravel raised money and now injects ads directly into your agent When moving fast, talking is the first thing to break Too much Discussion of the XOR swap trick – Heather Cafe Introduction to Spherical Harmonics for Graphics Programmers The Grand Line Building a Z-Machine in the worst possible language High-Level Rust: Getting 80% of the Benefits with 20% of the Pain GitHub - duguyue100/midnight-captain: Inspired by Midnight Commander, tailored to my taste. How to build a `git diff` driver · Jamie Tanna | Software Engineer Center for Responsible, Decentralized Intelligence at Berkeley The Local Universe’s Expansion Rate Is Clearer Than Ever, but Still Doesn’t Add Up - A new synthesis of astronomical measurements confirms a persistent mismatch that could point to physics beyond current models The air throughout our homes is infused with microplastics. But there are things you can do to breathe less of them The disturbing white paper Red Hat is trying to erase from the internet – OSnews The Future of Everything is Lies, I Guess: Annoyances ‘Abhorrent’: the inside story of the Polymarket gamblers betting millions on war Productive procrastination — Max van IJsselmuiden maps, territory and LMs 447 Terabytes per Square Centimetre at Zero Retention Energy: Non-Volatile Memory at the Atomic Scale on Fluorographane Show HN: Pardonned.com – A searchable database of US Pardons 20 Years on AWS and Never Not My Job The Seasons are Wrong Artemis II crew splashes down near San Diego after historic moon mission We gave an AI a 3 year retail lease in SF and asked it to make a profit | Andon Labs How a dancer with ALS used brainwaves to perform live On filing the corners off my MacBooks Installing every* Firefox extension OpenClaw’s memory is unreliable, and you don’t know when it will break Steve Blank Nowhere Is Safe Chimpanzees in Uganda locked in vicious 'civil war', say researchers watgo - a WebAssembly Toolkit for Go linux/Documentation/process/coding-assistants.rst at master · torvalds/linux GitHub - callumlocke/json-formatter: Makes JSON easy to read. Founding Product Engineer at Bild AI | Y Combinator A compelling title that is cryptic enough to get you to take action on it GitHub - Keychron/Keychron-Keyboards-Hardware-Design: Industrial design files for Keychron keyboards and mice. 100+ models with CAD assets in STEP, DXF, DWG, and PDF. Source-available, with commercial use allowed for original compatible accessories within the license terms. [ANNOUNCE] WireGuardNT v0.11 and WireGuard for Windows v0.6 Released 1D-Chess Helium Is Hard to Replace Cooperative Vectors Introduction | Evolve Keeping a Postgres queue healthy — PlanetScale Our response to the Axios developer tool compromise Do Americans read print books, e-books or audiobooks more? The Zettelkasten Method in Obsidian: A Practical Setup Guide Artemis II Is Competency Porn and We Are Starving For It WeakC4 Flight Viz — Cockpit View A Mexican surveillance giant you’ve never heard of is now watching the U.S. border Surelock: Deadlock-Free Mutexes for Rust RISC-V 101 – what is it and what does it mean for Canonical? | Ubuntu The Problem That Built an Industry How Much Linear Memory Access Is Enough? | Solidean Investigating Split Locks on x86-64 Simplest hash functions Sybilproof reputation mechanisms (2005) [pdf] What is a property? How Complex is my Code? Static code analysis in Kotlin — tools overview Toffoli gates are all you need PGLite evangelism dcmake: a new CMake debugger UI Clojure on Fennel part one: Persistent Data Structures Fragments: April 2 Python Release Python install manager 26.1 The Life and Death of the Book Review - Liberties Introducing Database Traffic Control — PlanetScale Bitcoin miners are losing $19,000 on every BTC produced as difficulty drops 7.8% God sleeps in the minerals Building slogbox Apple Silicon and Virtual Machines: Beating the 2 VM Limit Who was “Not Even Wrong” first? Pokemon Evolution Vs Darwinian Evolution The APL Programming Language Source Code
Boot Naked Linux · ... and another thing ...
2026-06-16 · via Hacker News

2026-05-19 c / linux

Starting up a Linux kernel to host one single process rather than a full on operating system ... and doing it in less than a second!

When I was a kid, computers weren’t coddled and left running 24/7, when you were done with them you switched them off, and when you wanted them again you just switched them on and within a second or so they’d be loading whatever was in their disk drive.

There was a brief moment in the early 2000s where the newly introduced SSDs made booting quick but as always the tech industry has taken up the slack until even a 16 core monster with a fast SSD still takes a minute to get its feet under it.

So I wanted to try an alternative. Keep the Linux kernel but strip away everything else I could. Here goes … well not quite here goes nothing, but here goes a lot less.

UPDATE: As per long tradition, while trying to fix a couple of details I found “Building a tiny Linux from scratch” which does most of what I do here but in Rust and a year ago, so that’s worth a look too.

Hello, World!

The first thing a Linux system does is run an “init” program of one sort or another which loads all the other processes and configurations and stuff. There’s nothing too special about this program, it’s just a regular executable or script, and there have been a few different approaches taken over the years in any case.

So we can write a new one in C … here’s init.c:

#include <stdio.h>
#include <stdlib.h>
#include <sys/reboot.h>

int main(int argc, char **argv) {
    fprintf(stderr, "Hello from init.c!");
    reboot(RB_POWER_OFF);
}

All it does is print a message, and then reboot the computer.

If our init process exits the kernel panics, so instead of busy waiting1 or sleeping forever or whatever, we use reboot(RB_POWER_OFF) to shut the virtual machine down in an orderly manner.

Making initrd

Modern Linux supports quite a complex multi-stage process. There’s a lot of resources out there, and a lot of them are 20 years out of date, and there have been changes but here’s a summary of how I think it works right now, as of 2026 and Linux 6.8 or thereabouts:

  • A boot loader runs with a kernel and a dummy filesystem called ‘initrd’.
  • The kernel attempt to unpack the ‘initrd’ file into an ‘initramfs’, a root filesystem in RAM which allows initialization tools to run.
    • it looks for a file called /init (or whatever is specified in the rdinit= kernel parameter)
    • if it exists, it runs it and that process takes over initialization.
  • Otherwise, it falls back to:
    • mount the root partition specified by the root= kernel parameter
    • mount the devtmpfs filesystem at /dev
    • run /init (or whatever is specified in the init= kernel parameter) from that.
  • Otherwise, or if the init process exits, it kernel panics.

Most modern distros use the first branch: quite a large initrd filesystem is provided so that modules and firmware can get loaded before attempting to boot the real filesystem. The initrd file which my PC boots from is 73MB and according to lsintramfs it contains 2163 files!

There’s a few examples of how to construct a filesystem with a replacement for init but I wanted to go even simpler and replace the entire initrd.

If we compile our example code statically, eg: containing all the libraries it needs, we can make our own initrd with only one file in it:

gcc -static init.c -o init

echo 'init' | cpio -o --format=newc | gzip -c > initrd

about cpio

cpio is a very weird and ancient program with a command line which makes tar look user-friendly. But let’s not worry about the details for now.

I will note that, later, if you get a kernel message:

Initramfs unpacking failed: no cpio magic

… it means that either the cpio format or the compression or something similar isn’t compatible with your kernel. The kernel will attempt to continue, but a later error message like:

check access for rdinit=/init failed: -2, ignoring

… means that either the initramfs didn’t happen or your binary is in the wrong place (-2 is -ENOENT which means file not found). You might also get a message about incompatible architectures. This message happens pretty early in the boot process, which attempts to continue anyway, so you’ll have to look back carefully. Whereas if you see:

Trying to unpack rootfs image as initramfs...

… and then nothing else, that’s a good sign. It never logs that it was successful until much later when it should hopefully say:

Run /init as init process

Incidentally, if you’re looking to pack many files into a cpio archive, you want something along the lines of:

(cd $SOURCE_DIR; find . | cpio -o -H newc) | gzip -c > $OUTPUT_FILE

The whole thing about piping in a list of files probably seems perverse but cpio is older than tar, older than shell filename globbing so perhaps we can forgive it

Virtualized

Getting this going on real hardware would involve an irritating amount of USB key swapping, so I’m using QEMU to make a virtual system to experiment with.

QEMU lets you boot from just a kernel and a filesystem image on the command line.

For now, rather than proper QEMU I’m using KVM to run a virtualized system. For full emulation, you can also run these examples with qemu-system-x86_64, or whatever the appropriate emulator is for your system. It’s slightly slower but otherwise works the same.

First we need a kernel, I’m just using the current kernel from my machine but the /boot/vmlinuz file is readable by root only so first we make a copy of it in our working directory and change its ownership:

sudo cp /boot/vmlinuz .
sudo chown $USER:$GROUP vmlinuz

If you can be bothered building your own cut-down kernel that’d be even better.

We now have our two binary files, the kernel vmlinuz and the init image initrd which contains only our program init. So we can boot our system with:

kvm -m 1G -nographic -kernel vmlinuz \
    -initrd initrd -append "console=ttyS0"

The -nographic and -append "console=ttyS0" options give us a terminal console to monitor stderr on rather than popping up a graphical console.

When the kernel starts up, it unpacks our initrd into a ram disk, and runs our init binary:

[    0.000000] Linux version 6.8.0-111-generic (buildd@lcy02-amd64-088)
[    0.000000] Command line: console=ttyS0
[    0.489390] Trying to unpack rootfs image as initramfs...
[    0.805419] Run /init as init process
Hello from init.c!
[    0.807535] reboot: Power down

(abbreviated)

Devices

Even if we don’t want any filesystems, we might want some permanent storage.

But at the time our /init runs, we haven’t mounted a root filesystem yet, so there’s no devices available! Devices are made available using a kernel mechanism called “devtmpfs” so the first thing we have to do is activate that. We can mount devtmpfs from our C program using mount("devtmpfs", "/dev", "devtmpfs", 0, NULL).

QEMU can present a host file as a block device on the guest using the -hda option, the file will appear to the guest as /dev/sda, so let’s modify our code from before to mount the devtmpfs, open /dev/sda and read the first few bytes from that file:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mount.h>
#include <sys/reboot.h>
#include <arpa/inet.h>

int main(int argc, char **argv) {
    fprintf(stderr, "Hello from init.c!\n");
    mount("devtmpfs", "/dev", "devtmpfs", 0, NULL);

    int fd = open("/dev/sda", O_RDWR);
    uint32_t buffer[2];
    read(fd, buffer, sizeof(buffer));
    close(fd);

    fprintf(stderr, "Read %08x %08x\n",
        ntohl(buffer[0]), ntohl(buffer[1]));

    reboot(RB_POWER_OFF);
}

(yeah, I know, proper C code needs to be scattered with return value checks and sensible reports of errno. I’ve left these out for clarity.)

Before we can run this we need a disk image. Let’s make some random bytes in a file:

dd if=/dev/random of=diskimage bs=1K count=1K

Then we can compile and run this like before:

gcc -static -o init init.c 

echo 'init' | cpio -o --format=newc | gzip -c > initrd

kvm -m 1G -nographic -kernel vmlinuz \
    -initrd initrd -append "console=ttyS0" \
    -no-reboot -hda diskimage

… and end up with output something like (edited for brevity):

[    0.000000] Linux version 6.8.0-111-generic (buildd@lcy02-amd64-088)
[    0.000000] Command line: console=ttyS0
[    0.010975] Memory: 975024K/1048056K available (22528K kernel code, 4438K rwdata,
               14412K rodata, 4924K init, 4788K bss, 72772K reserved, 0K cma-reserved)
[    0.493923] Trying to unpack rootfs image as initramfs...
[    0.712522] ata1.00: ATA-7: QEMU HARDDISK, 2.5+, max UDMA/100
[    0.713091] ata1.00: 2048 sectors, multi 16: LBA48 
[    0.816634] Run /init as init process
Hello from init.c!
Read 4463823c a5c37e77
[    0.819849] reboot: Power down

So there we go, less than a second from power-on to running a program and reading from disk.

Booting real hardware

There are a lot of conflicting instructions out there, and most of them seem to assume you’re trying to update the bootloader of the system you’re using, rather than adding EFI booting to a USB stick, but this is what worked for me:

  • plug in the USB key
  • use lsblk to make absolutely sure it’s the right one (in my case, it’s /dev/sda, but be careful as these commands will trash whatever drive you’re pointing them at.)
  • use dd if=/dev/zero of=/dev/sda bs=1M count=1 to clear the boot sector and partition table completely.
  • use sudo cfdisk to set up some partitions:
    • Format the device as “dos”
    • Create a partition of type “EFI” (hex id ef) with 512M size and mark it as bootable.
    • Create another partition for later, leave the type “Linux” (hex id 83) for now

It should end up looking something like:

                       Disk: /dev/sda
     Size: 3.76 GiB, 4037017600 bytes, 7884800 sectors
             Label: dos, identifier: 0xdeadd0d0

Device    Boot   Start     End Sectors Size Id Type
/dev/sda1 *       2048 1050623 1048576 512M ef EFI (FAT-12/16/32)   
/dev/sda2      1050624 7884799 6834176 3.3G 83 Linux

Plenty of places seem to say the partition table has to be of type GPT and the EFI partition of a special type within that, but this is what my Mint Linux installer USB looked like and this is what worked for me on this crappy laptop.

Now we can make the file systems on our partitions:

sudo mkfs.fat -F 32 /dev/sda1
sudo mkfs.ext3 /dev/sda2

In theory UEFI can load multiple files from multiple locations, and run a little script called startup.nsh, and stuff like that, but this laptop refused to pay any attention to any of that, and the only way to get it to boot happily was to create a file called /EFI/BOOT/BOOTX64.EFI.

So I needed to make a “unified kernel” which contains a loader stub, the kernel itself, and the initrd file which contains our program. We can do this with the ukify tool:

sudo apt install systemd-ukify systemd-boot-efi
ukify build --linux=vmlinuz --initrd=initrd

Then we just need to copy the new unified kernel file into the right place:

sudo mount /dev/sda1 /mnt
sudo mkdir -p /mnt/EFI/BOOT
sudo cp vmlinuz.unsigned.efi /mnt/EFI/BOOT/BOOTX64.EFI
sudo umount /mnt

… and go boot the USB stick!

And then realize that it reboots too quick to get a photo of, and add a sleep(5); in there. And then boot it again, and take a photo:

photo of message on a real laptop screen

Tiny Kernel

At the start of all this I just copied the Linux kernel from my running desktop: this is a Ubuntu install and pretty hefty as kernels go, with support for many things not relevant to our one-program-at-a-time project. It’s pretty easy to build a new Linux kernel though:

git clone https://github.com/torvalds/linux.git
cd linux
make tinyconfig
make menuconfig

This will get you to the linux kernel configuration menu, a wonderful text menu system with a thousand options which has been baffling new users for about 30 years now.

The linux kernel config menu

make tinyconfig switches off just about everything, too many things in fact. As per “Building a tiny Linux from scratch” some things have to be switched back on for our kernel to work.

Configuring

I’ve tried to lay these out in order to make it possible to pick your way through the menus:

  • “General setup”

    • “Local version”

      I set this to “tiny” to make it easier to tell which kernel is which

    • “Initial RAM filesystem and RAM disk (initramfs/initrd) support”

      More options will appear. I kept “Support initial ramdisk/ramfs compressed using gzip” and switched the others off, we might as well have only one decompressor in th kernel.

      “Initramfs source file(s)” is intriguing and I’ll come back to this.

    • Compiler optimization level

      I changed this to “Optimize for size (-Os)”

    • “Configure standard kernel features (expert users)”

      Never mind the scary name. This is both a toggle (hit space) and a nested menu (hit enter) which is massively confusing, and the contents are different depending on if it is switched on or off.

      • “Multiple users, groups and capabilities support”

        I’ve left this off for now because we don’t really need it.

      • “Enable support for printk”

        You can save a bit of space by switching this off, but Without it you won’t know why the kernel isn’t starting.

  • “64-bit kernel”

    Even the shittiest laptop I’ve got laying around here is 64 bit. If you pull something even older out of the junk pile, you might prefer 32 bit.

  • “Processor type and features”

    This depends on your target platform too, but I chose to turn on “Symmetric multi-processing support” and set “Maximum number of CPUs” to 4, but turn off the “Cluster scheduler support” and “Multi-core scheduler support”

  • “Enable the block layer”

    I think this is necessary to access the block devices like “/dev/sda1” but I turned off “Legacy autoloading support” and “Allow writing to mounted block devices”

  • “Executable file formats”

    • “Kernel support for ELF binaries”

      Our init program is compiled to ELF format, we could probably compile it to an older format but there’s no real advantage. I’ve left “Kernel support for scripts starting with #!” off though because there’s no shell where we’re going …

  • “Device Drivers”

    • “Generic Driver Options”

      • “Maintain a devtmpfs filesystem to mount at /dev”

        This is how we actually find devices on the system.

    • “Character devices”

      • “Enable TTY”

        We definiely need this to communicate with our program …

      • “Serial drivers”

        … and our ‘qemu’ emulation provides the text console as a fake serial device, so I turned on “8250/16550 and compatible serial support”

    • “Graphics support”

      • “Frame buffer devices”

        Just for fun I turned this on with just “Provide legacy /dev/fb* device”, it’ll be interesting to see if our program can actually do gasp graphics!

  • “File systems”

    We may not need them immediately, but I switched on “The Extended 3 (ext3) filesystem” and “The Extended 4 (ext4) filesystem”

    • “DOS/FAT/EXFAT/NT Filesystems”

      I also switched on “MSDOS fs support” and “VFAT (Windows-95) fs support” and “exFAT filesystem support”. It might be handy to be able to read more files out of that EFI partition.

  • “Kernel hacking”

    Again, don’t be put off by the menu name. I just went into “printk and dmesg options” and set “Show timing information on printks” which gives the printk messages nice timestamps.

If you want to make the kernel with a UEFI stub, you need to do things out of order. First go to “Power Management and ACPI options” and switch on “ACPI”. Then you can go back to “Processor type and features” and “EFI runtime service support” and “EFI stub support” should be active.

Building

Once you’ve waded through all that, you can just make -j 8 to build a new kernel. Depending on exactly what options you included, the compiled kernel should be 1-2 MB instead of the 16MB vendor kernel. Of course, the down-side is that you probably won’t know what feature you missed until your code tries to use it!

Is this worth bothering with?

Hard to say. It saves a little bit of space, boots a bit quicker, and removing unused parts of the kernel may well improve the security of a device since there’s less “attack surface” to go wrong.

There’s also the possibility of compiling in our initrd system, reducing even further our reliance on external files. That combined with the EFI loader stub option means we’d only need exactly one file to load our program from boot.

Also, we’re going to need to build our own kernel to target more interesting platforms.

TO BE CONTINUED

Next up: Forest for the trees, an adventure in structured data on our minimalist system …