Phase 4 — Systems Programming (M6-M7)¶
Two months. This is where C stops being a language and starts being a contract with the kernel. Every non-trivial C program on Linux is really a syscall driver with some computation glued between the traps. Until you feel that in your bones, you’re writing code that happens to compile — not code that survives production.
You’ve spent five months on the language itself: memory, pointers, tooling, data structures, generic patterns. Now you learn how a process is born, how it reads a file without lying to itself, how it dies gracefully, and how it talks to other processes without corrupting anyone’s state. This is the phase where study questions like “what happens between write(fd, buf, 4096) and the bytes hitting the disk?” become questions you actually enjoy.
Why this phase matters for you as an applied ML engineer¶
Inference servers are systems programs. TensorRT, ONNX Runtime, llama.cpp, vLLM — under the ML veneer, they are file descriptors, mmap, epoll, shared memory, and careful signal handling. When your model server hangs at 2 AM, the bug will almost never be in your Python glue. It will be a zombie process, a leaked fd, a SIGPIPE you didn’t catch, or a mmap region that wasn’t MAP_POPULATE’d and page-faulted through your p99. This phase is the one that lets you read those bug reports without freezing.
M6-M7 target¶
By the end of M7, you should:
Read
man 2 syscallfluently and predicterrnovalues from a code snippet without running it.Reach for
sigaction(notsignal) reflexively, and know why.Explain to a colleague, in one whiteboard, the difference between
fork,vfork,clone, andposix_spawn.Choose between pipes, POSIX shm, and Unix domain sockets for IPC based on the actual workload — not habit.
Read an
straceoutput and locate the misbehaving syscall in under 60 seconds.
What you will NOT do in this phase¶
No kernel module writing. That’s a distraction — the industry doesn’t hire junior kernel devs off C roadmaps.
No deep dive into obscure System V IPC. It’s mostly dead in 2026; we’ll mention it and move on.
No premature epoll. That’s Phase 5. Here you master blocking I/O first, because you can’t reason about async until you can reason about sync.
The stretch curriculum¶
# |
File |
What you get out of it |
|---|---|---|
1 |
User vs kernel mode, |
|
2 |
|
|
3 |
|
|
4 |
Pipes, FIFOs, POSIX shm, Unix domain sockets — the real 2026 winner |
|
5 |
procfs, sysfs, cgroups, namespaces, |
|
6 |
ELF, static vs dynamic, PIC/PIE, |
|
7 |
|
|
— |
Mini-shell + |
Time budget (M6-M7, 10-15h/week)¶
M6 weeks 1-2: Files 01-02. Get
straceandopen/read/writeinto muscle memory.M6 weeks 3-4: Files 03-04. Start the mini-shell project on weekends.
M7 weeks 1-2: Files 05-06. Ship v1 of the shell (fork/exec/pipes/redirects working).
M7 weeks 3-4: File 07 +
tail -fclone + polish shell (signals, background jobs).
Exit criteria — you don’t leave Phase 4 until you can honestly say yes to all of these¶
I can write a program that
forks a child, sets up a pipe,execs two commands with the pipe wired between them, and reaps both — from memory, no reference.I know why
signal()is a footgun and can name the three specific behaviorssigaction()gives you thatsignal()doesn’t.I have shipped a working mini-shell of at least 500 LOC that handles
|,<,>,&, andCtrl-Ccorrectly.I can produce a flamegraph of my own program and identify the top self-time function.
I have run
strace -con at least one real program and interpreted the output.I can explain what happens to a file descriptor across
forkandexec— and whatO_CLOEXECchanges.
What most people get wrong about this phase¶
They rush to epoll and io_uring. Don’t. You’ll spend a decade fighting concurrency bugs you don’t understand because you skipped the boring part where read() might return fewer bytes than you asked for and you didn’t write a wrapper for it. Blocking I/O first. Signals first. Process lifecycle first. The async stuff in Phase 5 will feel obvious after this — and terrifying if you skip ahead.
Anchor references for the whole phase¶
W. Richard Stevens, Advanced Programming in the UNIX Environment (APUE), 3rd ed. — still the reference. Chapters 3-10, 15 map directly onto this phase.
Michael Kerrisk, The Linux Programming Interface (TLPI) — the Linux-specific companion to APUE. More current on Linux-only features (
signalfd,epoll,namespaces).man 2 <syscall>— read it every time. Notman 3(that’s libc wrappers, different beast).Julia Evans’ zines (wizardzines.com) —
straceand Linux debugging zines are excellent quick refs.
Return to roadmap root · Next: 01_the_unix_syscall_model.md