Phase 04 — Linux Internals, GPU Drivers & Containers

Difficulty: ⭐⭐⭐⭐☆ | Estimated Time: 2 weeks Roles supported: Head of Engineering [GPU], Infrastructure/Platform Engineer, Driver-adjacent Runtime Engineer Hardware needed: a Linux machine or VM (Lima/Multipass/UTM on macOS, WSL2 on Windows, any cloud VM). No GPU required.


Why This Phase Exists

The JD requires "solid grounding in Linux internals, containers, GPU drivers." Between your CUDA program (Phase 02) and the silicon (Phase 01) sits a stack most engineers treat as superstition: libcuda.so, /dev/nvidia0, a kernel module, ioctls, mapped ring buffers. When a fleet GPU "falls off the bus," when a container can't see its GPU, when driver/CUDA version skew bricks a deployment at 2 a.m. — the engineers who can reason about this layer fix it; everyone else reboots and prays.

You will not write an NVIDIA driver. You will write a small, honest Linux character-device driver that exposes the same architectural pattern GPU drivers use — a /dev node, ioctl command submission, mmap-style buffer sharing — and you will build a container from scratch with namespaces and cgroups, then map exactly how a GPU passes through that boundary. After this, nvidia-container-toolkit and k8s device plugins (Phase 06) are obvious instead of magical.


Concepts

  • Kernel space vs user space; syscalls; what a "driver" actually is (callbacks registered with the kernel)
  • Character devices: major/minor numbers, /dev nodes, udev, file_operations
  • ioctl: the universal "weird device command" escape hatch — and the GPU's actual control path
  • The real NVIDIA stack: KMD (nvidia.ko) vs UMD (libcuda.so), command ring buffers, doorbells, why submission avoids syscalls in the hot path
  • GSP firmware, the open-gpu-kernel-modules split, nouveau vs proprietary
  • Driver/CUDA version skew: forward-compat matrix, the "driver too old" failure, fleet upgrade choreography
  • DMA and IOMMU; pinned memory from the kernel's point of view (Phase 02 Ch. 5 connection)
  • Namespaces (mount, PID, net, user) — what a container actually is
  • cgroups v2: cpu, memory, io controllers — and why GPUs have no cgroup controller (the gap that creates the entire GPU-sharing industry, Phase 06)
  • How a GPU enters a container: device nodes + library injection (nvidia-container-toolkit), CUDA_VISIBLE_DEVICES vs cgroup devices allow-lists
  • /proc, /sys, strace, lsof, dmesg — the debugging toolkit

Labs

Lab 01 — A GPU-Shaped Character Device Driver (C, Linux kernel module)

FieldValue
GoalWrite, build, load, and exercise a kernel module exposing /dev/fakegpu: ioctl command submission (ALLOC/SUBMIT/WAIT/INFO), a per-process "context," and a job queue — the GPU driver pattern in 300 lines.
Conceptsfile_operations, copy_to_user/copy_from_user, ioctl ABI design, per-fd state (the "GPU context"!), kernel logging, module lifecycle.
Steps1) make && sudo insmod fakegpu.ko, check dmesg. 2) Run the userspace client ./client — it allocates "buffers," submits "jobs," waits for completion. 3) strace ./client and see exactly the ioctl pattern strace-ing a real CUDA app shows. 4) Extensions: add a MAP command, enforce per-context quotas, simulate async completion with a kernel timer.
StackC, Linux kernel headers (linux-headers-$(uname -r)), any Linux VM
OutputLoadable .ko + client whose built-in asserts pass; an strace capture annotated against the real nvidia ioctl flow.
How to Test./client prints PASS for: context isolation (two fds don't see each other's buffers), quota enforcement, job completion ordering.
Talking PointsWhy per-fd state mirrors CUDA contexts; why real GPUs submit via mapped ring buffers + doorbells instead of one ioctl per kernel launch (syscall overhead vs Phase 02's ~5µs launch); what a poisoned context means at this layer.
Resume Bullet"Implemented a Linux character-device driver with ioctl-based command submission modeling GPU driver architecture (contexts, buffer registry, job queue); validated isolation and quota semantics with a userspace test client."
Extensionsmmap a shared "completion ring" to the client and poll it without syscalls (the doorbell/fence pattern); add a sysfs attribute exposing per-context stats (your first step toward Phase 10's metrics).

Lab 02 — Container From Scratch + GPU Passthrough Anatomy (C + shell)

FieldValue
GoalBuild a minimal container runtime in C — namespaces, pivot_root, cgroup v2 limits — then dissect how real runtimes inject GPUs.
Conceptsclone(2) flags, mount/PID/UTS/user namespaces, cgroup v2 filesystem API, device nodes in containers, what nvidia-container-toolkit actually does (hook → inject /dev/nvidia* + driver libs).
Steps1) make && sudo ./mininer run /bin/sh — you're PID 1 in an isolated rootfs. 2) Apply a 100 MB memory limit via cgroup v2; watch the OOM killer enforce it. 3) Walkthrough: trace nvidia-container-toolkit config and replicate its effect manually (bind-mount a fake /dev/fakegpu from Lab 01 into your container + allow it in the devices cgroup). 4) Write the one-page "how a GPU enters a container" explainer.
StackC (clone/unshare/mounts), cgroup v2 sysfs, your Lab 01 device
OutputWorking mini-runtime + the GPU-passthrough explainer (your team-training artifact).
How to TestInside the container: ps shows PID 1–2 only; hostname differs; memory hog gets OOM-killed at the limit; /dev/fakegpu works after injection but /dev/null outside the allow-list is denied.
Talking Points"A container is a process with opinions" — namespaces (visibility) vs cgroups (resources) vs capabilities (privilege); why GPU has no cgroup controller and what that implies for multi-tenancy (Phase 06's opening problem).
Resume Bullet"Built a minimal container runtime in C (namespaces, pivot_root, cgroups v2) and documented GPU device-injection mechanics, establishing the team's mental model for GPU container debugging."
ExtensionsAdd network namespace + veth pair; user-namespace UID mapping (rootless containers); compare your runtime's strace against runc's.

Deliverables Checklist

  • fakegpu.ko loads; client passes all asserts; you can explain every file_operations callback
  • Annotated strace: your client vs a real CUDA app (or the captured trace provided)
  • Mini-runtime: isolation + OOM demos reproduced
  • The "how a GPU enters a container" explainer written
  • You can narrate the KMD/UMD split and the ring-buffer submission path from memory

Interview Relevance

  • "What happens, at the OS level, when you call cudaLaunchKernel?"
  • "Why doesn't CUDA_VISIBLE_DEVICES provide isolation? What does?"
  • "A container sees the GPU but CUDA init fails. Debug it." (driver/library version skew, device node perms, the toolkit hook)
  • "Why is there no GPU cgroup controller, and what do people do instead?"
  • "Driver upgrade across a 10k-GPU fleet — walk me through the plan."