The Linux Kernel¶
The kernel is the one program that is always running. It owns the hardware, decides which process runs on which CPU, hands out memory, mediates every file and network operation, and enforces the security boundaries everything else relies on. Almost every hard production problem — latency spikes, OOM kills, container escapes, packet drops, "disk is full but du says otherwise" — eventually ends up here.
This page is the hub for kernel documentation. Related pages:
Table of Contents¶
- What the Kernel Is
- Versions and the Release Process
- The Boot Process
- The Kernel Command Line
- System Calls
- Process Management and Scheduling
- Memory Management
- VFS, Filesystems, and the Block Layer
- Networking Stack
- Device Drivers and the Device Model
- Kernel Modules
- Kernel Interfaces: /proc, /sys, and sysctl
- Namespaces and cgroups (Container Primitives)
- eBPF
- Security
- Logging, Observability, and Tracing
- Crashes, Oopses, Panics, and kdump
- Live Patching and kexec
- Tuning Reference
- Rust in the Kernel
- The Source Tree
- Contributing Upstream
- Cheat Sheet
- Further Reading
What the Kernel Is¶
Linux is a monolithic, modular kernel:
- Monolithic — the scheduler, memory manager, filesystems, network stack, and drivers all run in one privileged address space. A call between subsystems is a function call, not a message. Fast, but a bug in any driver can take down the whole system.
- Modular — most of that code can be built as loadable kernel modules (
.ko) and inserted or removed at runtime without rebooting.
Contrast with microkernels (seL4, QNX, MINIX 3), which push drivers and filesystems into user-space servers for isolation at the cost of IPC overhead.
Kernel space vs user space¶
+----------------------------------------------------------+
| User space (ring 3) |
| bash, systemd, nginx, containerd, kubelet, glibc/musl |
+---------------------- system call interface -------------+
| Kernel space (ring 0) |
| syscalls | VFS | scheduler | MM | net | security (LSM) |
| block layer | device model | IPC | eBPF | tracing |
| arch-specific code | device drivers |
+----------------------------------------------------------+
| Hardware: CPU, RAM, disks, NICs, GPUs, firmware |
+----------------------------------------------------------+
The CPU enforces the split with privilege levels (rings on x86, exception levels on ARM). User processes can't touch hardware or other processes' memory directly — they ask the kernel through system calls.
What the kernel is not¶
"Linux" in casual use means a whole OS. Strictly, Linux is only the kernel. The C library (glibc, musl), init system (systemd, OpenRC), shell, coreutils, and package manager are separate projects that a distribution bundles together.
Versions and the Release Process¶
uname -r # 6.12.0-55.el10.x86_64
uname -a # everything, incl. build date and arch
cat /proc/version # compiler and build info
Version string anatomy¶
6.12.0-55.el10.x86_64
| Part | Meaning |
|---|---|
6 |
Major — bumped when the minor number "gets big", no special meaning |
12 |
Minor — each mainline release |
0 |
Stable patch level (6.12.7 = 7th stable update) |
-55.el10 |
Distro release/build (here RHEL 10) |
x86_64 |
Architecture |
Release cycle¶
- Merge window (~2 weeks) — Linus pulls new features from subsystem maintainers.
-rc1…-rc7/-rc8— one release candidate a week; fixes only.- Final release — roughly every 9–10 weeks.
Kernel lines¶
| Line | What it is |
|---|---|
| Mainline | Linus's tree, latest features |
| Stable | Mainline + backported fixes, maintained until the next couple of releases |
| Longterm (LTS) | One release a year (usually the last of the year) maintained for years. Kernel.org cut the default LTS support window to ~2 years in 2023; the Civil Infrastructure Platform (CIP) and enterprise distros maintain specific branches much longer |
| Distro kernels | RHEL, Ubuntu, SUSE etc. freeze a base version and backport fixes and features for a decade. A RHEL 5.14 kernel can contain code from much newer upstream releases — the version number alone tells you little |
Check what's current at https://www.kernel.org/.
The Boot Process¶
Firmware (UEFI / BIOS)
└─> Bootloader (GRUB2, systemd-boot, U-Boot)
└─> Kernel image (vmlinuz) + initramfs loaded into RAM
└─> Kernel decompresses, initializes CPU, memory, drivers
└─> Unpacks initramfs as a tmpfs root, runs /init
└─> initramfs finds the real root (LVM, LUKS, RAID, NFS...)
└─> switch_root to real /
└─> /sbin/init (systemd) becomes PID 1
Files in /boot¶
| File | Purpose |
|---|---|
vmlinuz-<ver> |
Compressed kernel image (bzImage on x86) |
initramfs-<ver>.img / initrd.img-<ver> |
Early user space archive (cpio, compressed) |
config-<ver> |
The .config the kernel was built with |
System.map-<ver> |
Symbol → address table (used by older debugging tools) |
loader/entries/*.conf |
BLS entries (Fedora/RHEL) |
Initramfs tools by distro¶
| Distro family | Tool | Inspect |
|---|---|---|
| RHEL/Fedora | dracut -f |
lsinitrd |
| Debian/Ubuntu | update-initramfs -u |
lsinitramfs |
| Arch | mkinitcpio -P |
lsinitcpio |
Rebuild the initramfs whenever you change storage drivers, LUKS/LVM setup, or module options that must apply in early boot.
Boot timing¶
systemd-analyze # firmware / loader / kernel / initrd / userspace
systemd-analyze blame
systemd-analyze critical-chain
dmesg | head -50 # earliest kernel messages
The Kernel Command Line¶
Parameters passed by the bootloader configure the kernel (and initramfs, and systemd) before anything else runs.
cat /proc/cmdline
Common parameters¶
| Parameter | Effect |
|---|---|
root=UUID=... |
Root filesystem |
ro / rw |
Mount root read-only initially (fsck) or read-write |
quiet, loglevel=N |
Console verbosity |
init=/bin/bash |
Replace PID 1 — classic emergency root shell |
systemd.unit=rescue.target / emergency.target |
Boot to rescue/emergency mode |
rd.break |
Drop to a shell inside the initramfs (RHEL root password reset) |
console=ttyS0,115200 |
Serial console (VMs, cloud, headless) |
nomodeset |
Disable kernel modesetting (graphics trouble) |
crashkernel=auto / crashkernel=256M |
Reserve memory for kdump |
mitigations=off |
Disable CPU vulnerability mitigations (perf over security — avoid on shared hosts) |
isolcpus=, nohz_full=, rcu_nocbs= |
Isolate CPUs for latency-sensitive workloads |
transparent_hugepage=never |
Disable THP (some databases recommend) |
intel_iommu=on iommu=pt |
IOMMU for PCI passthrough / VFIO |
selinux=0, enforcing=0, apparmor=0 |
LSM control (prefer permissive mode over disabling) |
module_blacklist=nouveau |
Prevent a module from loading |
lockdown=integrity |
Enable kernel lockdown |
panic=10 |
Reboot 10 s after a panic |
Making changes persistent¶
# RHEL / Fedora (BLS)
sudo grubby --update-kernel=ALL --args="console=ttyS0,115200"
sudo grubby --update-kernel=ALL --remove-args="quiet"
sudo grubby --info=DEFAULT
# Debian / Ubuntu
sudoedit /etc/default/grub # GRUB_CMDLINE_LINUX_DEFAULT="..."
sudo update-grub
# Generic GRUB2
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
# systemd-boot / UKI
sudoedit /etc/kernel/cmdline
For a one-off, press e at the GRUB menu, edit the linux line, and boot with Ctrl-x.
System Calls¶
A system call is the controlled doorway from user space into the kernel. glibc wraps most of them (open() → openat syscall), and the CPU switches privilege level via syscall (x86_64) or svc (arm64).
Core families:
| Area | Examples |
|---|---|
| Processes | clone, clone3, execve, exit_group, wait4, kill |
| Files | openat, read, write, close, statx, mmap, ioctl |
| Memory | mmap, munmap, brk, mprotect, madvise |
| Network | socket, bind, connect, accept4, sendmsg, recvmsg |
| Async I/O | epoll_wait, io_uring_setup, io_uring_enter |
| Namespaces | unshare, setns, clone with CLONE_NEW* flags |
| Security | seccomp, capset, landlock_* |
strace -f -e trace=openat,execve ./program # what is it doing?
strace -c ./program # syscall counts and time
ausyscall --dump | less # syscall number table (audit package)
vDSO — a small kernel-provided shared library mapped into every process so hot calls like clock_gettime avoid a real mode switch.
io_uring (5.1+) — shared ring buffers between user space and kernel for batched async I/O with very few syscalls. Powerful, but has been a major source of kernel CVEs; several distros and Google disable it by default (kernel.io_uring_disabled sysctl).
Process Management and Scheduling¶
Tasks¶
To the kernel, processes and threads are both tasks (struct task_struct). Threads are just tasks that share an address space, file table, and so on — the difference is which CLONE_* flags were passed.
Process states as shown by ps/top:
| State | Meaning |
|---|---|
R |
Running or runnable |
S |
Interruptible sleep (waiting on an event) |
D |
Uninterruptible sleep (usually I/O — counts toward load average, can't be killed) |
T / t |
Stopped / traced |
Z |
Zombie — exited, parent hasn't wait()ed |
I |
Idle kernel thread |
Kernel threads appear in ps with names in brackets: [kworker/0:1], [ksoftirqd/0], [kswapd0], [rcu_sched].
ps -eo pid,ppid,stat,ni,pri,cls,comm --sort=-pcpu | head
cat /proc/<pid>/status
cat /proc/<pid>/sched
Scheduler classes¶
Checked in priority order — a runnable task in a higher class always wins.
| Class | Policies | Use |
|---|---|---|
stop |
— | Internal (CPU hotplug, migration) |
deadline |
SCHED_DEADLINE |
Hard runtime/period guarantees |
rt |
SCHED_FIFO, SCHED_RR |
Real-time priorities 1–99 |
fair |
SCHED_OTHER/SCHED_NORMAL, SCHED_BATCH, SCHED_IDLE |
Everything normal |
ext |
SCHED_EXT |
BPF-defined schedulers (6.12+) |
idle |
— | Runs when nothing else can |
The fair class was CFS (Completely Fair Scheduler) from 2.6.23 until 6.6, when it was replaced by EEVDF (Earliest Eligible Virtual Deadline First), which handles latency more predictably. sched_ext (6.12) lets you load an entire scheduling policy as a BPF program.
PREEMPT_RT — the real-time patchset was fully merged in 6.12, so mainline kernels can be built fully preemptible.
Controlling priority and placement¶
nice -n 10 cmd # start with lower priority (-20 highest, 19 lowest)
renice -n 5 -p <pid>
chrt -f 50 cmd # SCHED_FIFO priority 50
chrt -p <pid> # show policy
taskset -c 2,3 cmd # pin to CPUs 2-3
taskset -cp <pid>
ionice -c3 cmd # idle I/O class
Load average¶
uptime shows 1/5/15-minute averages of tasks that are runnable or in D state — on Linux, heavy I/O wait raises load even with idle CPUs. Compare against nproc, and look at vmstat 1 (r and b columns) and PSI (below) for the real story.
Pressure Stall Information (PSI)¶
cat /proc/pressure/cpu
cat /proc/pressure/memory
cat /proc/pressure/io
some avg10=4.20 means at least one task was stalled 4.2 % of the last 10 s. full means all non-idle tasks were stalled. Per-cgroup versions exist in cgroup v2 (cpu.pressure, etc.). systemd-oomd uses memory PSI.
Interrupts¶
cat /proc/interrupts # hardware IRQ counts per CPU
cat /proc/softirqs # NET_RX, TIMER, BLOCK, RCU...
cat /proc/irq/<n>/smp_affinity_list
Hardware interrupts do minimal work and defer the rest to softirqs, tasklets, or workqueues (kworker threads). High si in top usually means network receive processing.
Memory Management¶
Virtual memory¶
Every process gets its own virtual address space. The kernel and MMU map virtual pages (usually 4 KiB) to physical frames via page tables; the TLB caches translations. Pages are only backed by physical memory when first touched (demand paging).
cat /proc/<pid>/maps # memory regions
cat /proc/<pid>/smaps_rollup
pmap -x <pid>
Reading memory usage¶
free -h
cat /proc/meminfo
vmstat 1
/proc/meminfo field |
Meaning |
|---|---|
MemFree |
Truly unused — normally small, and that's fine |
MemAvailable |
Estimate of memory available for new work without swapping — the number to watch |
Cached / Buffers |
Page cache — reclaimable file data |
Dirty / Writeback |
Modified pages not yet on disk |
AnonPages |
Process heap/stack memory |
Shmem |
tmpfs and shared memory (not freely reclaimable) |
Slab / SReclaimable |
Kernel object caches (dentries, inodes) |
Committed_AS |
Total memory promised to processes |
"Linux ate my RAM" — the page cache uses idle memory and gives it back under pressure. See https://www.linuxatemyram.com/.
Reclaim and swap¶
When free memory drops below watermarks, kswapd reclaims pages in the background; if that's not enough, allocating tasks do direct reclaim (latency!). Clean file pages are dropped, dirty ones written back, anonymous pages swapped out.
vm.swappiness(0–200, default 60) — bias between reclaiming anonymous memory (swap) vs file cache.- MGLRU (multi-gen LRU, 6.1+) — improved page aging; check
/sys/kernel/mm/lru_gen/enabled. - zswap — compressed cache in front of a swap device. zram — compressed RAM block device used as swap (Fedora default).
swapon --show
cat /proc/swaps
Overcommit¶
vm.overcommit_memory |
Behaviour |
|---|---|
0 (default) |
Heuristic — refuses only obviously impossible allocations |
1 |
Always allow (Redis recommends) |
2 |
Strict: commit limit = swap + RAM × vm.overcommit_ratio% |
The OOM killer¶
When memory is exhausted and reclaim fails, the OOM killer picks a victim by oom_score (mostly memory footprint) and kills it.
cat /proc/<pid>/oom_score
echo -500 | sudo tee /proc/<pid>/oom_score_adj # -1000 = never kill, 1000 = kill first
journalctl -k | grep -i -A20 'out of memory'
dmesg -T | grep -i 'killed process'
In systemd units: OOMScoreAdjust=-500. In containers, a cgroup hitting memory.max triggers an OOM kill inside that cgroup only — this is Kubernetes' OOMKilled (exit code 137).
User-space OOM daemons (systemd-oomd, earlyoom) act earlier, based on PSI, to avoid the long thrash before the kernel OOM killer fires.
Huge pages¶
| Type | Notes |
|---|---|
| HugeTLB (2 MiB / 1 GiB) | Pre-reserved via vm.nr_hugepages, used explicitly (DPDK, databases, VMs) |
| Transparent Huge Pages (THP) | Kernel promotes memory automatically; khugepaged compacts in the background |
cat /sys/kernel/mm/transparent_hugepage/enabled # [always] madvise never
grep -i huge /proc/meminfo
Many databases (MongoDB, older Redis, Oracle) recommend madvise or never for THP due to latency spikes from compaction.
NUMA¶
On multi-socket systems memory is local to a CPU node; remote access is slower.
numactl --hardware
numastat -p <pid>
numactl --cpunodebind=0 --membind=0 cmd
VFS, Filesystems, and the Block Layer¶
The Virtual File System¶
The VFS is a common abstraction over every filesystem — ext4, XFS, Btrfs, NFS, procfs, tmpfs, FUSE all present the same open/read/write/stat interface. Key objects:
| Object | Represents |
|---|---|
| superblock | A mounted filesystem |
| inode | A file's metadata (owner, mode, size, block map) — not its name |
| dentry | A name → inode link; cached in the dcache |
| file | An open instance (position, flags) — what a file descriptor points to |
"Everything is a file" works because devices, pipes, sockets, and pseudo-files all implement the VFS operations.
cat /proc/filesystems # filesystems the kernel supports right now
findmnt # mount tree
stat file
ls -l /proc/<pid>/fd
lsof +L1 # deleted-but-open files (the "df full, du not" mystery)
Page cache and writeback¶
Reads and writes go through the page cache. Writes dirty pages in memory; flusher threads write them back based on:
| sysctl | Meaning |
|---|---|
vm.dirty_background_ratio / _bytes |
Start background writeback |
vm.dirty_ratio / _bytes |
Writers block and flush synchronously |
vm.dirty_expire_centisecs |
Age at which dirty data must be written |
On machines with lots of RAM, use the _bytes variants so a percentage doesn't mean tens of GiB of dirty data.
sync
echo 3 | sudo tee /proc/sys/vm/drop_caches # drop clean caches — benchmarking only
Block layer¶
filesystem -> bio -> blk-mq (per-CPU software queues -> hardware queues) -> driver -> device
blk-mq (multi-queue) is the only block path in modern kernels. I/O schedulers:
| Scheduler | Best for |
|---|---|
none |
NVMe and fast SSDs — let the device decide |
mq-deadline |
General-purpose default for SATA SSD/HDD |
bfq |
Desktop interactivity, fairness on slow disks |
kyber |
Latency targets on fast devices |
cat /sys/block/nvme0n1/queue/scheduler
echo mq-deadline | sudo tee /sys/block/sda/queue/scheduler
lsblk -o NAME,ROTA,SCHED,SIZE,TYPE,MOUNTPOINTS
iostat -xz 1
Persist with a udev rule:
# /etc/udev/rules.d/60-iosched.rules
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq"
Stacked block devices — device-mapper (LVM, LUKS/dm-crypt, dm-multipath, dm-verity) and md (software RAID) — sit between filesystem and hardware.
Networking Stack¶
NIC -> driver (NAPI polling) -> [XDP] -> sk_buff -> tc ingress -> netfilter PREROUTING
-> routing -> INPUT -> socket -> application
-> FORWARD -> POSTROUTING -> tc egress -> driver -> NIC
Concepts:
sk_buff— the kernel's packet structure.- NAPI — switches from interrupt-per-packet to polling under load.
- GRO/GSO/TSO — batch packets to cut per-packet overhead (
ethtool -k eth0). - RSS/RPS/RFS — spread receive processing across CPUs.
- Netfilter — hooks for filtering/NAT. nftables is the modern front end;
iptableson current distros is usuallyiptables-nft, a compatibility layer. Kubernetes kube-proxy uses iptables, IPVS, or (newer) nftables mode; Cilium replaces it with eBPF. - conntrack — stateful connection tracking (
conntrack -L,nf_conntrack_max). A full conntrack table drops new connections withnf_conntrack: table fullin dmesg. - tc — traffic control: qdiscs (
fq_codeldefault,fqfor BBR), shaping, eBPF hooks. - XDP — eBPF at the driver, before an
sk_buffexists. Line-rate filtering/load balancing. - Network namespaces, veth, bridges, VXLAN — the building blocks of container networking.
ip -s link
ip route
ss -tulpn # listening sockets
ss -s # socket summary
nstat -az | grep -i drop
cat /proc/net/softnet_stat # 2nd column = drops due to backlog
ethtool -S eth0 | grep -iE 'drop|err|miss'
sysctl net.ipv4.tcp_congestion_control # cubic default; bbr available
Device Drivers and the Device Model¶
Drivers bind to devices on a bus (PCI, USB, platform, I²C, virtio). The device model exposes this tree in /sys, and udev (systemd-udevd) reacts to kernel uevents to create /dev nodes, load modules, and apply rules.
lspci -nnk # PCI devices + driver in use + available modules
lsusb -t
ls -l /sys/class/net/eth0/device/driver
udevadm info /dev/sda
udevadm monitor --kernel --udev
dmesg | grep -i firmware
Device files:
ls -l /dev/sda /dev/null
# brw-rw---- 1 root disk 8, 0 ... b = block, major 8, minor 0
# crw-rw-rw- 1 root root 1, 3 ... c = character
cat /proc/devices
Firmware blobs (linux-firmware) live in /lib/firmware and are loaded by drivers on demand.
Kernel Modules¶
lsmod # loaded modules
modinfo br_netfilter # description, params, deps, signature
sudo modprobe br_netfilter # load with dependencies
sudo modprobe -r br_netfilter # unload
systool -vm <module> # current parameter values (sysfsutils)
cat /sys/module/<mod>/parameters/<param>
ls /lib/modules/$(uname -r)/
sudo depmod -a # rebuild dependency map
Prefer modprobe over insmod/rmmod — it resolves dependencies and reads config.
Persistent configuration¶
# Load at boot
echo -e "overlay\nbr_netfilter" | sudo tee /etc/modules-load.d/k8s.conf
# Module options
echo "options kvm_intel nested=1" | sudo tee /etc/modprobe.d/kvm.conf
# Blacklist (prevents autoload, not explicit modprobe)
echo "blacklist nouveau" | sudo tee /etc/modprobe.d/blacklist-nouveau.conf
# Truly prevent loading
echo "install usb_storage /bin/false" | sudo tee /etc/modprobe.d/no-usb-storage.conf
Rebuild the initramfs if the module is loaded during early boot.
Out-of-tree modules and DKMS¶
DKMS rebuilds third-party modules (NVIDIA, ZFS, VirtualBox, some Wi-Fi) automatically on every kernel update.
dkms status
sudo dkms autoinstall
Module signing and Secure Boot¶
With Secure Boot, the kernel enters lockdown and refuses unsigned modules. Sign out-of-tree modules with a Machine Owner Key:
sudo mokutil --import /var/lib/dkms/mok.pub # enrol on next boot
mokutil --sb-state
cat /sys/kernel/security/lockdown
module.sig_enforce=1 requires signatures even without Secure Boot.
Kernel Interfaces: /proc, /sys, and sysctl¶
| Interface | Content |
|---|---|
/proc |
Processes (/proc/<pid>/) plus legacy system info (meminfo, cpuinfo, interrupts) |
/proc/sys |
Tunables — what sysctl reads and writes |
/sys |
Structured device/driver/kernel object tree (one value per file) |
/sys/kernel/debug |
debugfs — developer interfaces, unstable |
/sys/kernel/tracing |
tracefs — ftrace |
/sys/fs/cgroup |
cgroup v2 hierarchy |
/sys/fs/bpf |
Pinned BPF objects |
Useful /proc/<pid> entries: cmdline, environ, status, limits, fd/, maps, smaps_rollup, stack (kernel stack, root), wchan, cgroup, ns/, mountinfo, io.
sysctl¶
sysctl -a | grep -i forward
sysctl net.ipv4.ip_forward
sudo sysctl -w net.ipv4.ip_forward=1 # runtime only
# Persistent: drop-in files, applied at boot by systemd-sysctl
echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/90-forward.conf
sudo sysctl --system # reload all
Load order: /usr/lib/sysctl.d/, /run/sysctl.d/, /etc/sysctl.d/, then /etc/sysctl.conf — later files win for the same key. Name files with numeric prefixes.
Note that some net.* sysctls are per network namespace — setting them on the host doesn't change them inside a container. Kubernetes exposes "safe" namespaced sysctls via securityContext.sysctls.
Namespaces and cgroups (Container Primitives)¶
A container is just a process with its own namespaces (what it can see), cgroups (what it can use), plus capabilities, seccomp, and an LSM profile (what it may do).
Namespaces¶
| Namespace | Isolates | Since |
|---|---|---|
mnt |
Mount table | 2.4.19 |
uts |
Hostname, domain name | 2.6.19 |
ipc |
SysV IPC, POSIX message queues | 2.6.19 |
pid |
Process IDs (PID 1 inside) | 2.6.24 |
net |
Interfaces, routes, firewall, sockets | 2.6.29 |
user |
UID/GID mappings, capabilities | 3.8 |
cgroup |
View of the cgroup hierarchy | 4.6 |
time |
CLOCK_MONOTONIC/BOOTTIME offsets |
5.6 |
lsns
ls -l /proc/$$/ns
sudo unshare --pid --fork --mount-proc --net --uts bash # mini container
sudo nsenter -t <pid> -n ip addr # enter a container's netns
sudo nsenter -t <pid> -a # all namespaces
ip netns add test && ip netns exec test ip link
Debug a Kubernetes pod's network from the node: find the container PID (crictl inspect <id> | jq .info.pid) then nsenter -t <pid> -n tcpdump -i eth0.
cgroups v2¶
A single unified hierarchy at /sys/fs/cgroup (the default on all current major distros; v1 is deprecated). systemd owns the tree: slices → services/scopes → processes. Kubernetes uses kubepods.slice.
stat -fc %T /sys/fs/cgroup # cgroup2fs = v2
systemd-cgls
systemd-cgtop
cat /proc/<pid>/cgroup
cat /sys/fs/cgroup/cgroup.controllers
Key controller files:
| File | Controls |
|---|---|
cpu.max |
quota period — e.g. 50000 100000 = 0.5 CPU (k8s CPU limit) |
cpu.weight |
Relative share (k8s CPU request) |
cpu.stat |
Includes nr_throttled, throttled_usec — CPU throttling evidence |
memory.max |
Hard limit → OOM kill (k8s memory limit) |
memory.high |
Throttle/reclaim threshold |
memory.current, memory.stat, memory.events |
Usage and OOM counts |
io.max |
Per-device bandwidth/IOPS limits |
pids.max |
Fork bomb protection |
*.pressure |
Per-cgroup PSI |
# Limit a one-off command
systemd-run --scope -p MemoryMax=512M -p CPUQuota=50% stress-ng --vm 1
# Set on a running service
sudo systemctl set-property nginx.service MemoryMax=1G
eBPF¶
eBPF lets you load small, verified programs into the kernel and attach them to hooks — without writing a module or risking a crash. The verifier proves the program terminates and only accesses memory safely; a JIT compiles it to native code.
Attach points: kprobes/kretprobes, tracepoints, uprobes, perf events, XDP, tc, cgroup hooks, sockets, LSM hooks, sched_ext. Data moves through maps (hash, array, ring buffer, per-CPU).
Uses:
- Observability — bpftrace, BCC tools, Pixie, Parca, Pyroscope
- Networking — Cilium, Calico eBPF data plane, Katran load balancer
- Security — Falco, Tetragon, Tracee, BPF LSM
sudo bpftool prog list
sudo bpftool map list
sudo bpftool feature probe | less
# Who is opening which files?
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args.filename)); }'
# (older bpftrace versions use args->filename)
# Syscall counts by process
sudo bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'
# BCC tools (package: bcc-tools / bpfcc-tools)
sudo execsnoop # new processes
sudo opensnoop
sudo biolatency # block I/O latency histogram
sudo tcpconnect
sudo runqlat # scheduler run-queue latency
BTF + CO-RE (Compile Once, Run Everywhere) let one compiled BPF program run across kernel versions; check /sys/kernel/btf/vmlinux exists.
Security¶
Privilege model¶
- Capabilities split root into ~40 privileges (
CAP_NET_ADMIN,CAP_SYS_ADMIN,CAP_NET_BIND_SERVICE...).CAP_SYS_ADMINis effectively "root lite" — avoid granting it.
getpcaps <pid>
grep Cap /proc/<pid>/status && capsh --decode=<hex>
getcap /usr/bin/ping
sudo setcap cap_net_bind_service=+ep ./server
- seccomp — filters which syscalls a process may make. Docker/containerd apply a default profile; Kubernetes
seccompProfile: RuntimeDefault. - no_new_privs — prevents gaining privileges via setuid exec.
Linux Security Modules (LSMs)¶
| LSM | Type | Where |
|---|---|---|
| SELinux | Mandatory access control, label-based | RHEL, Fedora, Android |
| AppArmor | MAC, path-based profiles | Ubuntu, Debian, SUSE |
| Landlock | Unprivileged self-sandboxing (5.13+) | Applications restrict themselves |
| Yama | ptrace restrictions |
kernel.yama.ptrace_scope |
| Lockdown | Restricts root from modifying the running kernel | Secure Boot |
| BPF LSM | Policy written in eBPF | Tetragon, KRSI |
cat /sys/kernel/security/lsm
getenforce && sestatus
aa-status
CPU vulnerabilities¶
grep -r . /sys/devices/system/cpu/vulnerabilities/
Shows the mitigation status for Spectre, Meltdown, MDS, Retbleed, etc.
Hardening sysctls¶
# /etc/sysctl.d/80-hardening.conf
kernel.kptr_restrict = 2 # hide kernel pointers
kernel.dmesg_restrict = 1 # dmesg for root only
kernel.yama.ptrace_scope = 1 # ptrace only descendants
kernel.unprivileged_bpf_disabled = 1
net.core.bpf_jit_harden = 2
kernel.kexec_load_disabled = 1 # one-way switch
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
fs.protected_fifos = 2
fs.protected_regular = 2
fs.suid_dumpable = 0
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.tcp_syncookies = 1
Test before deploying — unprivileged_userns_clone-style restrictions or BPF restrictions can break rootless containers, browsers' sandboxes, and some monitoring agents. kernel-hardening-checker (a Python tool) audits a config, cmdline, and sysctls against recommendations.
Logging, Observability, and Tracing¶
Kernel log¶
dmesg -T # human timestamps
dmesg -l err,warn
dmesg -w # follow
journalctl -k # kernel messages, this boot
journalctl -k -b -1 # previous boot (needs persistent journal)
journalctl -k -p err
Enable persistent journal so you can read logs from before a crash: sudo mkdir -p /var/log/journal && sudo systemctl restart systemd-journald (or Storage=persistent in journald.conf).
printk log levels¶
| Level | Name | Level | Name | |
|---|---|---|---|---|
| 0 | EMERG | 4 | WARNING | |
| 1 | ALERT | 5 | NOTICE | |
| 2 | CRIT | 6 | INFO | |
| 3 | ERR | 7 | DEBUG |
cat /proc/sys/kernel/printk # console / default / minimum / boot-default
sudo dmesg -n 4 # only <4 to console
Dynamic debug turns on pr_debug() messages for specific code:
echo 'module nfs +p' | sudo tee /sys/kernel/debug/dynamic_debug/control
The toolbox¶
| Question | Tool |
|---|---|
| Where is CPU time going (incl. kernel)? | perf top, perf record -g + perf report, flame graphs |
| Hardware counters, IPC, cache misses | perf stat -d |
| What kernel functions run, and in what order? | ftrace (trace-cmd, /sys/kernel/tracing) |
| Custom ad-hoc questions | bpftrace |
| Packaged answers | BCC tools |
| Syscalls of one process | strace (heavy overhead) or perf trace |
| Is it CPU, memory, or I/O pressure? | PSI, vmstat, iostat, mpstat, pidstat |
| Where is a stuck process stuck? | cat /proc/<pid>/stack, echo w > /proc/sysrq-trigger |
ftrace quick start¶
cd /sys/kernel/tracing
cat available_tracers
echo function_graph > current_tracer
echo vfs_read > set_graph_function
echo 1 > tracing_on; sleep 1; echo 0 > tracing_on
head -50 trace
echo nop > current_tracer
# Friendlier front end
sudo trace-cmd record -p function_graph -g do_sys_openat2 -- ls
sudo trace-cmd report | less
perf quick start¶
sudo perf top -g
sudo perf record -F 99 -a -g -- sleep 30
sudo perf report --stdio | head -100
sudo perf stat -e cycles,instructions,cache-misses -a sleep 5
sudo perf sched latency
kernel.perf_event_paranoid controls what unprivileged users can profile.
Crashes, Oopses, Panics, and kdump¶
| Event | What happens |
|---|---|
| WARN | Kernel detected something unexpected, prints a stack, carries on |
| Oops | Kernel fault (bad pointer). The offending task is killed; the system may limp on in an unreliable state |
| BUG | Assertion failure — usually becomes an oops |
| Panic | Unrecoverable; system halts (or reboots per kernel.panic) |
| Soft lockup | A CPU stuck in kernel mode > ~20 s without scheduling |
| Hard lockup | A CPU not handling interrupts (NMI watchdog) |
| Hung task | A task in D state > 120 s (kernel.hung_task_timeout_secs) |
Reading an oops¶
Key lines: the reason (BUG: kernel NULL pointer dereference), RIP: (faulting function + offset, e.g. ext4_foo+0x1a/0x80 [ext4]), the Tainted: flags, and the Call Trace:. Decode offsets to source lines with scripts/decode_stacktrace.sh and a matching vmlinux with debuginfo.
Taint flags¶
cat /proc/sys/kernel/tainted # 0 = clean
| Bit | Value | Letter | Meaning |
|---|---|---|---|
| 0 | 1 | P | Proprietary module loaded |
| 1 | 2 | F | Module force-loaded |
| 7 | 128 | D | Kernel died recently (oops/BUG) |
| 9 | 512 | W | Warning issued |
| 12 | 4096 | O | Out-of-tree module |
| 13 | 8192 | E | Unsigned module |
| 14 | 16384 | L | Soft lockup occurred |
| 15 | 32768 | K | Live-patched |
Upstream developers generally won't debug reports from kernels tainted with proprietary modules. tools/debugging/kernel-chktaint in the source tree decodes the number.
Panic behaviour¶
kernel.panic = 10 # reboot 10 s after panic (0 = hang forever)
kernel.panic_on_oops = 1 # treat oops as panic (good with kdump)
kernel.softlockup_panic = 0
kernel.hung_task_panic = 0
kdump¶
kdump reserves memory for a second "capture" kernel. On a panic, the system kexecs into it and saves the crashed memory image to /var/crash.
# RHEL/Fedora
sudo dnf install kexec-tools crash kernel-debuginfo
sudo grubby --update-kernel=ALL --args="crashkernel=auto" # or a size; RHEL 9+ uses kdumpctl reset-crashkernel
sudo systemctl enable --now kdump
kdumpctl status
# Debian/Ubuntu
sudo apt install kdump-tools crash linux-image-$(uname -r)-dbgsym
# Test (this WILL crash the box)
echo 1 | sudo tee /proc/sys/kernel/sysrq
echo c | sudo tee /proc/sysrq-trigger
# Analyse
sudo crash /usr/lib/debug/lib/modules/$(uname -r)/vmlinux /var/crash/<dir>/vmcore
# crash> bt log ps kmem -i files <pid>
Magic SysRq¶
cat /proc/sys/kernel/sysrq # bitmask; 1 = all, 176 = sync+remount+reboot
echo 1 | sudo tee /proc/sys/kernel/sysrq
echo w | sudo tee /proc/sysrq-trigger # dump blocked (D-state) tasks
echo t | sudo tee /proc/sysrq-trigger # dump all tasks
echo m | sudo tee /proc/sysrq-trigger # memory info
echo l | sudo tee /proc/sysrq-trigger # backtrace all CPUs
On a frozen machine with a keyboard: Alt+SysRq+ R E I S U B — unRaw keyboard, tErminate, kIll, Sync, Unmount (remount ro), reBoot.
Live Patching and kexec¶
Live patching¶
Apply security fixes to the running kernel without a reboot by redirecting functions (via ftrace) to patched versions. Taints the kernel with K.
| Vendor | Service |
|---|---|
| Red Hat | kpatch (kpatch list, delivered as kpatch-patch-* RPMs) |
| Canonical | Livepatch (canonical-livepatch status, Ubuntu Pro) |
| SUSE | kGraft/kernel-livepatch (SLE Live Patching) |
| Oracle | Ksplice |
cat /sys/kernel/livepatch/*/enabled
Live patches buy time between reboots; they don't replace them — not every fix can be live-patched.
kexec¶
Boot a new kernel directly from the running one, skipping firmware and bootloader — much faster reboots on servers with long POST.
sudo kexec -l /boot/vmlinuz-<ver> --initrd=/boot/initramfs-<ver>.img --reuse-cmdline
sudo systemctl kexec
Is a reboot pending?¶
# Running vs newest installed
uname -r; ls -t /boot/vmlinuz-* | head -1
dnf needs-restarting -r # RHEL/Fedora
cat /var/run/reboot-required # Debian/Ubuntu
Tuning Reference¶
Always measure before and after; most defaults are good. These are the ones that come up most in real infrastructure.
Kubernetes / container nodes¶
# /etc/modules-load.d/k8s.conf
overlay
br_netfilter
# /etc/sysctl.d/90-k8s.conf
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
fs.inotify.max_user_watches = 524288
fs.inotify.max_user_instances = 8192
net.netfilter.nf_conntrack_max = 1048576
too many open files from kubelet/log shippers is often inotify instances, not file descriptors.
Elasticsearch / OpenSearch¶
vm.max_map_count = 262144 # required minimum
vm.swappiness = 1 # or disable swap
High-connection servers¶
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_tw_reuse = 1
net.core.netdev_max_backlog = 16384
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
Files and processes¶
fs.file-max = 2097152 # system-wide (usually already huge on 64-bit)
kernel.pid_max = 4194304
Per-process limits are separate: ulimit -n, /etc/security/limits.d/, or LimitNOFILE= in systemd units.
Memory¶
vm.swappiness = 10
vm.dirty_background_bytes = 268435456 # 256 MiB
vm.dirty_bytes = 1073741824 # 1 GiB
vm.min_free_kbytes = 131072 # bigger cushion for bursty allocs
Profiles¶
tuned (RHEL/Fedora) bundles sysctls, I/O schedulers, CPU governors and more:
tuned-adm list
tuned-adm active
sudo tuned-adm profile throughput-performance # or latency-performance, virtual-guest
Rust in the Kernel¶
Initial Rust support landed in 6.1 (December 2022) — the first new language accepted alongside C. It provides safe abstractions over kernel APIs so drivers can be written with Rust's memory-safety guarantees, eliminating whole classes of bugs (use-after-free, data races) that dominate kernel CVEs.
Status and landmarks:
- Lives in
rust/(bindings and thekernelcrate) with drivers underdrivers/. - Real drivers have shipped: Android binder, the QR-code panic screen for DRM, the ASIX PHY driver, NVMe and null block reference drivers, and the Nova NVIDIA GPU driver (in progress), plus the Asahi Apple GPU driver out of tree.
- Built with a specific minimum
rustcplusbindgen; check withmake LLVM=1 rustavailable, enable withCONFIG_RUST=y. - At the 2025 Maintainers Summit, Rust support was declared no longer experimental.
The model is C and Rust coexisting long-term — subsystems opt in; nobody is rewriting the core in Rust.
The Source Tree¶
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git — or browse with cross-references at https://elixir.bootlin.com/.
| Directory | Contents |
|---|---|
arch/ |
Architecture-specific code (x86, arm64, riscv, ...) incl. boot code |
block/ |
Block layer and I/O schedulers |
crypto/ |
Crypto API and algorithms |
Documentation/ |
The official docs (rendered at docs.kernel.org) |
drivers/ |
Device drivers — the majority of the code |
fs/ |
VFS and filesystems |
include/ |
Headers; include/uapi/ is the user-space ABI |
init/ |
Boot-time initialisation (start_kernel() in init/main.c) |
io_uring/ |
io_uring |
ipc/ |
SysV IPC, message queues |
kernel/ |
Core: scheduler (kernel/sched/), signals, fork, BPF, tracing, cgroups |
lib/ |
Generic helpers |
mm/ |
Memory management |
net/ |
Networking stack |
rust/ |
Rust support |
samples/ |
Example modules |
scripts/ |
Build tooling, checkpatch.pl, get_maintainer.pl |
security/ |
LSMs (SELinux, AppArmor, Landlock, ...) |
sound/ |
ALSA |
tools/ |
User-space tools that live with the kernel (perf, bpftool, selftests) |
virt/ |
KVM common code |
MAINTAINERS |
Who owns what |
The user-space ABI is sacred — "we do not break user space." Internal kernel APIs, by contrast, change freely, which is why out-of-tree modules break between versions.
For building from source, see Compiling a Custom Linux Kernel.
Checking a running kernel's config¶
grep CONFIG_BPF_SYSCALL /boot/config-$(uname -r)
zcat /proc/config.gz | grep CONFIG_IKCONFIG # if built with CONFIG_IKCONFIG_PROC
Contributing Upstream¶
Development happens over email, not merge requests.
git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
# Work on the relevant subsystem tree (see MAINTAINERS "T:" lines)
git commit -s # Signed-off-by (DCO) is mandatory
git format-patch -1 --base=auto
./scripts/checkpatch.pl 0001-*.patch
./scripts/get_maintainer.pl 0001-*.patch # who to send it to
git send-email --to=... --cc=... 0001-*.patch
b4 (pip install b4) simplifies preparing series and pulling patches from https://lore.kernel.org/.
Good starting points: the KernelNewbies site, fixing checkpatch or compiler warnings in drivers/staging/, running and fixing kselftests (make -C tools/testing/selftests run_tests), testing release candidates and reporting regressions, and the Linux Foundation Mentorship Program.
Read first: Documentation/process/submitting-patches.rst and Documentation/process/coding-style.rst.
Cheat Sheet¶
# Identity
uname -r # running version
cat /proc/cmdline # boot parameters
cat /proc/sys/kernel/tainted # taint state
# Logs
dmesg -T -l err,warn
journalctl -k -b -1
# Modules
lsmod; modinfo <m>; modprobe <m>; modprobe -r <m>
# Tunables
sysctl <key>; sysctl -w <key>=<v>; sysctl --system
# Resources and pressure
free -h; vmstat 1; iostat -xz 1; cat /proc/pressure/*
# Processes
ps -eo pid,stat,wchan:32,comm | awk '$2 ~ /D/' # stuck in D state
cat /proc/<pid>/stack
# Containers
lsns; nsenter -t <pid> -n <cmd>; systemd-cgls; cat /proc/<pid>/cgroup
# Tracing
perf top -g; bpftrace -l 'tracepoint:syscalls:*'; trace-cmd record ...
# Hardware/drivers
lspci -nnk; lsblk; udevadm info /dev/<dev>
# Security
cat /sys/kernel/security/lsm
grep -r . /sys/devices/system/cpu/vulnerabilities/
Further Reading¶
- docs.kernel.org — official documentation, including the admin guide and
kernel-parameters - kernelnewbies.org — per-release changelogs in plain English (
LinuxChanges) - LWN.net — the best ongoing coverage of kernel development
- elixir.bootlin.com — cross-referenced source browser
- brendangregg.com — performance analysis, flame graphs, eBPF tools
- Linux Kernel Development — Robert Love (dated but still the clearest overview)
- Understanding the Linux Kernel — Bovet & Cesati
- Systems Performance, 2nd ed. and BPF Performance Tools — Brendan Gregg
- Linux Device Drivers, 3rd ed. — free online, old APIs but sound concepts
man 7pages:namespaces,cgroups,capabilities,bpf-helpers,sched,proc(man 5 proc)