Linux Kernel Internals: The Cybersecurity Core
📘 The Complete Tutorial
Introduction: Why Kernel Internals Matter for Cybersecurity
The Linux kernel is the trusted computing base of every Linux system. If an attacker compromises the kernel, they own the entire machine. If a defender hardens the kernel, they protect everything above it.
Understanding kernel internals is not optional for serious cybersecurity work—it's the foundation of:
- Rootkit detection (recognizing kernel-level persistence)
- Exploit development (understanding syscall paths, memory layouts)
- Kernel hardening (seccomp, namespaces, LSMs like SELinux/AppArmor)
- Forensics (analyzing
/proc,/sys, kernel logs, crash dumps)
This course takes you from kernel source to custom system calls.
Module 01: Linux Kernel Source Tree
The kernel source is organized as:
linux/
├── arch/ # Architecture-specific code (x86, arm64, riscv)
├── block/ # Block layer (I/O scheduling)
├── certs/ # Signature verification keys
├── crypto/ # Cryptographic APIs
├── drivers/ # Device drivers (the largest directory)
├── fs/ # Filesystems (ext4, btrfs, proc, sysfs)
├── include/ # Header files
├── init/ # Kernel initialization
├── ipc/ # Inter-process communication
├── kernel/ # Core kernel (scheduler, signals, sys calls)
├── lib/ # Utility functions
├── mm/ # Memory management
├── net/ # Networking stack
├── samples/ # Example code
├── scripts/ # Build scripts
├── security/ # Security modules (SELinux, AppArmor, smack)
├── sound/ # Audio subsystem
├── tools/ # User-space tools (perf, bpf)
└── virt/ # Virtualization (KVM)
Key files:
kernel/sched/core.c— Scheduler corekernel/fork.c— Process creationfs/proc/—/procfilesystemarch/x86/entry/syscalls/— Syscall tables
Module 02: Kernel Compilation
Building the kernel from source:
1# Download source 2wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.8.tar.xz 3tar xf linux-6.8.tar.xz 4cd linux-6.8 5 6# Configure 7make menuconfig # TUI configurator 8make xconfig # GUI configurator 9make defconfig # Default config for your arch 10 11# Build 12make -j$(nproc) # Compile with all cores 13make modules_install # Install modules to /lib/modules/ 14make install # Install kernel to /boot/
Module 03: Kernel Configuration
.config controls what gets compiled:
| Option | Meaning |
|---|---|
y | Built into the kernel image (vmlinuz) |
m | Built as a loadable module (.ko) |
n | Not compiled |
Important configs for security:
CONFIG_SECURITY_SELINUX=yCONFIG_SECURITY_APPARMOR=yCONFIG_KASLR=y(Kernel Address Space Layout Randomization)CONFIG_STACKPROTECTOR=yCONFIG_DEBUG_KERNEL=y
Module 04: Boot Process
The boot sequence on x86:
- BIOS/UEFI — Hardware initialization, finds boot device
- Bootloader (GRUB2) — Loads kernel image (
vmlinuz) and initramfs - Kernel decompression —
vmlinuzis a compressedvmlinux startup_32()/startup_64()— Architecture-specific setupstart_kernel()— The C entry point (init/main.c)rest_init()— Spawnskernel_init(PID 1, becomes/sbin/init)- Userspace init — systemd, sysvinit, or custom init
Module 05: Kernel Initialization
start_kernel() in init/main.c is the "main()" of the kernel. It initializes:
- IRQs and timers —
init_IRQ(),time_init() - Memory management —
mm_init() - Scheduler —
sched_init() - VFS —
vfs_caches_init() - Root filesystem —
prepare_namespace()
Kernel command line: Parameters passed by the bootloader (quiet, init=/bin/bash, nokaslr, rdinit=).
Module 06: task_struct
Every process/thread is represented by a struct task_struct in the kernel:
1struct task_struct { 2 pid_t pid; // Process ID 3 pid_t tgid; // Thread group ID 4 volatile long state; // TASK_RUNNING, TASK_INTERRUPTIBLE, etc. 5 void *stack; // Kernel stack 6 struct mm_struct *mm; // Memory descriptor 7 struct files_struct *files; // Open files 8 struct signal_struct *signal; // Signal handlers 9 struct list_head tasks; // Linked list of all tasks 10 cputime_t utime, stime; // User/system CPU time 11 char comm[TASK_COMM_LEN]; // Command name 12 // ... 100+ more fields 13};
Accessing current task: current macro expands to get_current() which reads the task_struct from the per-CPU stack.
Module 07: Scheduler
The Linux scheduler is the Completely Fair Scheduler (CFS):
- Goal: Give each runnable task a "fair" share of CPU time
- Metric:
vruntime(virtual runtime) — weighted by priority - Data structure: Red-black tree ordered by
vruntime - Preemption: Tasks are preempted when a higher-priority task wakes or their time slice expires
Scheduling classes (highest to lowest priority):
stop_sched_class— Migration threads (must run NOW)dl_sched_class— Deadline scheduling (real-time)rt_sched_class— Real-time FIFO/RRfair_sched_class— CFS (normal processes)idle_sched_class— Idle task
Module 08: Process Management
Creating a process:
fork()→sys_clone()→kernel_clone()→copy_process()copy_process()duplicatestask_struct,mm_struct, copies page tables (COW)
Exiting a process:
exit()→do_exit()→ sets state toTASK_DEAD, releases resources- Parent must call
wait()→do_wait()→ reaps zombie
The init process (PID 1):
- Adopts orphaned children
- Reaps zombies whose parents died
- Cannot be killed (even
kill -9fails)
Module 09: Kernel Threads
Kernel threads are processes that run only in kernel space:
- No userspace memory (
mm = NULL) - Created by:
kthread_create()orkthread_run() - Examples:
ksoftirqd,kworker,kswapd,kjournald
1struct task_struct *kthread_create(int (*threadfn)(void *data), 2 void *data, 3 const char namefmt[], ...);
Module 10: Interrupts
Hardware interrupts are handled by the Interrupt Descriptor Table (IDT):
- Top half (hard IRQ): Fast, minimal work, runs with interrupts disabled
- Bottom half: Deferred work — softirqs, tasklets, workqueues
Registration:
1request_irq(unsigned int irq, irq_handler_t handler, 2 unsigned long flags, const char *name, void *dev);
Important for security: Interrupt handlers run in atomic context — no sleeping, no kmalloc(GFP_KERNEL).
Module 11: System Calls
System calls are the gateway from userspace to kernel:
x86-64 syscall path:
- Userspace:
syscallinstruction - CPU enters ring 0, loads
MSR_LSTAR→entry_SYSCALL_64 swapgs— switch to kernel GS segment (per-CPU data)- Save registers to stack
- Look up syscall in
sys_call_table[rax] - Call handler
- Restore registers,
sysret
Adding a syscall requires updating:
arch/x86/entry/syscalls/syscall_64.tblinclude/linux/syscalls.h- Kernel function implementation
Module 12: VFS (Virtual File System)
VFS provides a unified interface for all filesystems:
User call: open("/etc/passwd", O_RDONLY)
↓
VFS: sys_open() → lookup dentry → inode → file_operations
↓
Filesystem driver: ext4, btrfs, proc, sysfs, tmpfs...
↓
Block layer / Page cache / Device driver
Key structures:
struct super_block— Filesystem instancestruct inode— File metadata (permissions, size, timestamps)struct dentry— Directory entry (name → inode mapping)struct file— Open file instance (file descriptor in kernel)
Module 13: Filesystems
Linux supports 100+ filesystems:
| Filesystem | Type | Use Case |
|---|---|---|
| ext4 | Disk | General purpose |
| XFS | Disk | Large files, high throughput |
| btrfs | Disk | Snapshots, checksums |
| proc | Virtual | Process info (/proc/[pid]/) |
| sysfs | Virtual | Kernel objects (/sys/class/) |
| tmpfs | RAM | Fast temporary files |
| devtmpfs | Virtual | Device nodes (/dev/) |
Mounting: mount -t ext4 /dev/sda1 /mnt → vfs_kern_mount() → filesystem-specific fill_super().
Module 14: Device Drivers
Linux classifies devices into three types:
| Type | Interface | Examples |
|---|---|---|
| Character | Byte stream | Terminals, serial ports, /dev/null |
| Block | Random access, buffered | Disks, SSDs (/dev/sda) |
| Network | Packet-based | Ethernet, WiFi |
Driver model:
struct device— Hardware devicestruct driver— Software driverstruct bus_type— Connection (PCI, USB, platform)
Module 15: Kernel Modules
Kernel modules (.ko files) extend the kernel without recompiling:
1#include <linux/module.h> 2#include <linux/kernel.h> 3 4static int __init mymod_init(void) { 5 printk(KERN_INFO "Module loaded\n"); 6 return 0; 7} 8 9static void __exit mymod_exit(void) { 10 printk(KERN_INFO "Module removed\n"); 11} 12 13module_init(mymod_init); 14module_exit(mymod_exit); 15MODULE_LICENSE("GPL");
Commands:
insmod hello.ko— Load modulermmod hello— Remove modulelsmod— List loaded modulesmodprobe— Load with dependency resolutiondmesg— View kernel log
Module 16: Workqueues
Workqueues defer work to process context (where sleeping is allowed):
1#include <linux/workqueue.h> 2 3static void my_work_handler(struct work_struct *work) { 4 // Can sleep here! 5} 6 7static DECLARE_WORK(my_work, my_work_handler); 8 9// Schedule from interrupt context: 10schedule_work(&my_work);
Types:
system_wq— Shared, unboundsystem_highpri_wq— High priorityalloc_workqueue()— Custom queue with attributes
Module 17: Timers
Kernel timers are soft timers — they fire in interrupt context:
1#include <linux/timer.h> 2 3struct timer_list my_timer; 4 5void timer_callback(struct timer_list *t) { 6 printk("Timer fired!\n"); 7} 8 9timer_setup(&my_timer, timer_callback, 0); 10mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000)); // 1 second
High-resolution timers (hrtimers): nanosecond precision for real-time needs.
Module 18: Locks
The kernel runs in a preemptible, multi-CPU, interrupt-driven environment. Locks are essential.
Locking rules:
- Never sleep while holding a spinlock
- Always acquire locks in the same order (prevent deadlocks)
- Keep critical sections short
Module 19: Spinlocks
Spinlocks busy-wait until the lock is available. Used when sleeping is not allowed:
1#include <linux/spinlock.h> 2 3spinlock_t my_lock; 4spin_lock_init(&my_lock); 5 6spin_lock(&my_lock); // Disables preemption on UP, spins on SMP 7// Critical section 8spin_unlock(&my_lock);
Variants:
spin_lock_irqsave()— Save and disable interruptsspin_lock_bh()— Disable bottom halvesread_lock()/write_lock()— Reader-writer spinlocks
Module 20: RCU (Read-Copy-Update)
RCU is a lock-free synchronization mechanism optimized for read-mostly data:
- Readers: No locks, no atomics, just
rcu_read_lock()/rcu_read_unlock() - Writers: Copy the data, modify the copy, then atomically update the pointer
- Grace period: Wait until all pre-existing readers finish
1rcu_read_lock(); 2p = rcu_dereference(global_ptr); // Safe read 3// Use p 4rcu_read_unlock(); 5 6// Writer: 7new_p = kmalloc(sizeof(*new_p), GFP_KERNEL); 8*new_p = *old_p; 9// modify new_p 10rcu_assign_pointer(global_ptr, new_p); 11synchronize_rcu(); // Wait for grace period 12kfree(old_p);
Use cases: Routing tables, process lists, kernel data structures with many readers.
Module 21: Atomic Operations
Kernel atomics are architecture-specific but portable:
1#include <linux/atomic.h> 2 3atomic_t counter = ATOMIC_INIT(0); 4atomic_inc(&counter); 5int val = atomic_read(&counter); 6 7// 64-bit: 8atomic64_t big_counter; 9atomic64_set(&big_counter, 42);
Bit operations:
1set_bit(0, &my_flags); // Atomically set bit 0 2test_and_set_bit(1, &flags); // Test old value, then set
Module 22: Kernel Debugging
Tools and techniques:
| Tool | Purpose |
|---|---|
printk() / pr_debug() | The kernel's printf — check with dmesg |
dynamic debug | Enable/disable pr_debug() at runtime |
BUG() / WARN() | Trigger oops/panic with stack trace |
KDB | In-kernel debugger (break into running kernel) |
KGDB | GDB over serial/network for remote debugging |
ftrace | Function tracer — trace-cmd, kernelshark |
perf | Performance counters and profiling |
kprobes | Dynamic instrumentation — breakpoints in kernel code |
eBPF | Safe, verified programs for tracing and filtering |
Oops analysis: When the kernel crashes, it prints an oops with register state, stack trace, and offending instruction. Decode with scripts/decodecode.
🛠 Hands-On: Kernel Module Progression
The following code files progress from a simple hello module to a custom system call. All code follows modern Linux kernel conventions (tested against 5.x–6.x APIs).
Download all files: linux_kernel_hands_on.tar.gz (or individual files below)
Step 1: hello_kernel.c
A minimal loadable kernel module.
Build: make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
Download: hello_kernel.c | Makefile
Step 2: module_params.c
Passing arguments to a kernel module at load time.
Download: module_params.c | Makefile
Step 3: proc_fs.c
Creating a /proc entry that exposes kernel data to userspace.
Download: proc_fs.c | Makefile
Step 4: char_device.c
A complete character device driver with read(), write(), and ioctl().
Download: char_device.c | Makefile
Step 5: custom_syscall.c
A custom system call implementation with installation instructions.
Download: custom_syscall.c | Makefile | README_SYSCALL.md
Let me generate all the code files now:
Linux Kernel Internals: The Cybersecurity Core
🎯 Meta Title (58 chars)
Linux Kernel Internals: From Source Tree to Custom Syscalls
📝 Meta Description (158 chars)
Master Linux kernel internals: compilation, boot process, scheduler, VFS, device drivers, kernel modules, RCU, spinlocks, and build custom syscalls with hands-on code.
🔑 SEO Keywords
Linux kernel internals, kernel module programming, Linux kernel compilation, task_struct scheduler, Linux device drivers, kernel syscall, proc filesystem, character device driver, kernel debugging, spinlock RCU, kernel boot process, VFS filesystems, kernel workqueues, kernel timers, kernel atomic operations
📘 The Complete Tutorial
Introduction: Why Kernel Internals Matter for Cybersecurity
The Linux kernel is the trusted computing base of every Linux system. If an attacker compromises the kernel, they own the entire machine. If a defender hardens the kernel, they protect everything above it.
Understanding kernel internals is not optional for serious cybersecurity work—it's the foundation of:
- Rootkit detection (recognizing kernel-level persistence)
- Exploit development (understanding syscall paths, memory layouts)
- Kernel hardening (seccomp, namespaces, LSMs like SELinux/AppArmor)
- Forensics (analyzing
/proc,/sys, kernel logs, crash dumps)
This course takes you from kernel source to custom system calls.
Module 01: Linux Kernel Source Tree
The kernel source is organized as:
linux/
├── arch/ # Architecture-specific code (x86, arm64, riscv)
├── block/ # Block layer (I/O scheduling)
├── certs/ # Signature verification keys
├── crypto/ # Cryptographic APIs
├── drivers/ # Device drivers (the largest directory)
├── fs/ # Filesystems (ext4, btrfs, proc, sysfs)
├── include/ # Header files
├── init/ # Kernel initialization
├── ipc/ # Inter-process communication
├── kernel/ # Core kernel (scheduler, signals, sys calls)
├── lib/ # Utility functions
├── mm/ # Memory management
├── net/ # Networking stack
├── samples/ # Example code
├── scripts/ # Build scripts
├── security/ # Security modules (SELinux, AppArmor, smack)
├── sound/ # Audio subsystem
├── tools/ # User-space tools (perf, bpf)
└── virt/ # Virtualization (KVM)
Key files:
kernel/sched/core.c— Scheduler corekernel/fork.c— Process creationfs/proc/—/procfilesystemarch/x86/entry/syscalls/— Syscall tables
Module 02: Kernel Compilation
Building the kernel from source:
1# Download source 2wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.8.tar.xz 3tar xf linux-6.8.tar.xz 4cd linux-6.8 5 6# Configure 7make menuconfig # TUI configurator 8make xconfig # GUI configurator 9make defconfig # Default config for your arch 10 11# Build 12make -j$(nproc) # Compile with all cores 13make modules_install # Install modules to /lib/modules/ 14make install # Install kernel to /boot/
Module 03: Kernel Configuration
.config controls what gets compiled:
| Option | Meaning |
|---|---|
y | Built into the kernel image (vmlinuz) |
m | Built as a loadable module (.ko) |
n | Not compiled |
Important configs for security:
CONFIG_SECURITY_SELINUX=yCONFIG_SECURITY_APPARMOR=yCONFIG_KASLR=y(Kernel Address Space Layout Randomization)CONFIG_STACKPROTECTOR=yCONFIG_DEBUG_KERNEL=y
Module 04: Boot Process
The boot sequence on x86:
- BIOS/UEFI — Hardware initialization, finds boot device
- Bootloader (GRUB2) — Loads kernel image (
vmlinuz) and initramfs - Kernel decompression —
vmlinuzis a compressedvmlinux startup_32()/startup_64()— Architecture-specific setupstart_kernel()— The C entry point (init/main.c)rest_init()— Spawnskernel_init(PID 1, becomes/sbin/init)- Userspace init — systemd, sysvinit, or custom init
Module 05: Kernel Initialization
start_kernel() in init/main.c is the "main()" of the kernel. It initializes:
- IRQs and timers —
init_IRQ(),time_init() - Memory management —
mm_init() - Scheduler —
sched_init() - VFS —
vfs_caches_init() - Root filesystem —
prepare_namespace()
Kernel command line: Parameters passed by the bootloader (quiet, init=/bin/bash, nokaslr, rdinit=).
Module 06: task_struct
Every process/thread is represented by a struct task_struct in the kernel:
1struct task_struct { 2 pid_t pid; // Process ID 3 pid_t tgid; // Thread group ID 4 volatile long state; // TASK_RUNNING, TASK_INTERRUPTIBLE, etc. 5 void *stack; // Kernel stack 6 struct mm_struct *mm; // Memory descriptor 7 struct files_struct *files; // Open files 8 struct signal_struct *signal; // Signal handlers 9 struct list_head tasks; // Linked list of all tasks 10 cputime_t utime, stime; // User/system CPU time 11 char comm[TASK_COMM_LEN]; // Command name 12 // ... 100+ more fields 13};
Accessing current task: current macro expands to get_current() which reads the task_struct from the per-CPU stack.
Module 07: Scheduler
The Linux scheduler is the Completely Fair Scheduler (CFS):
- Goal: Give each runnable task a "fair" share of CPU time
- Metric:
vruntime(virtual runtime) — weighted by priority - Data structure: Red-black tree ordered by
vruntime - Preemption: Tasks are preempted when a higher-priority task wakes or their time slice expires
Scheduling classes (highest to lowest priority):
stop_sched_class— Migration threads (must run NOW)dl_sched_class— Deadline scheduling (real-time)rt_sched_class— Real-time FIFO/RRfair_sched_class— CFS (normal processes)idle_sched_class— Idle task
Module 08: Process Management
Creating a process:
fork()→sys_clone()→kernel_clone()→copy_process()copy_process()duplicatestask_struct,mm_struct, copies page tables (COW)
Exiting a process:
exit()→do_exit()→ sets state toTASK_DEAD, releases resources- Parent must call
wait()→do_wait()→ reaps zombie
The init process (PID 1):
- Adopts orphaned children
- Reaps zombies whose parents died
- Cannot be killed (even
kill -9fails)
Module 09: Kernel Threads
Kernel threads are processes that run only in kernel space:
- No userspace memory (
mm = NULL) - Created by:
kthread_create()orkthread_run() - Examples:
ksoftirqd,kworker,kswapd,kjournald
1struct task_struct *kthread_create(int (*threadfn)(void *data), 2 void *data, 3 const char namefmt[], ...);
Module 10: Interrupts
Hardware interrupts are handled by the Interrupt Descriptor Table (IDT):
- Top half (hard IRQ): Fast, minimal work, runs with interrupts disabled
- Bottom half: Deferred work — softirqs, tasklets, workqueues
Registration:
1request_irq(unsigned int irq, irq_handler_t handler, 2 unsigned long flags, const char *name, void *dev);
Important for security: Interrupt handlers run in atomic context — no sleeping, no kmalloc(GFP_KERNEL).
Module 11: System Calls
System calls are the gateway from userspace to kernel:
x86-64 syscall path:
- Userspace:
syscallinstruction - CPU enters ring 0, loads
MSR_LSTAR→entry_SYSCALL_64 swapgs— switch to kernel GS segment (per-CPU data)- Save registers to stack
- Look up syscall in
sys_call_table[rax] - Call handler
- Restore registers,
sysret
Adding a syscall requires updating:
arch/x86/entry/syscalls/syscall_64.tblinclude/linux/syscalls.h- Kernel function implementation
Module 12: VFS (Virtual File System)
VFS provides a unified interface for all filesystems:
User call: open("/etc/passwd", O_RDONLY)
↓
VFS: sys_open() → lookup dentry → inode → file_operations
↓
Filesystem driver: ext4, btrfs, proc, sysfs, tmpfs...
↓
Block layer / Page cache / Device driver
Key structures:
struct super_block— Filesystem instancestruct inode— File metadata (permissions, size, timestamps)struct dentry— Directory entry (name → inode mapping)struct file— Open file instance (file descriptor in kernel)
Module 13: Filesystems
Linux supports 100+ filesystems:
| Filesystem | Type | Use Case |
|---|---|---|
| ext4 | Disk | General purpose |
| XFS | Disk | Large files, high throughput |
| btrfs | Disk | Snapshots, checksums |
| proc | Virtual | Process info (/proc/[pid]/) |
| sysfs | Virtual | Kernel objects (/sys/class/) |
| tmpfs | RAM | Fast temporary files |
| devtmpfs | Virtual | Device nodes (/dev/) |
Mounting: mount -t ext4 /dev/sda1 /mnt → vfs_kern_mount() → filesystem-specific fill_super().
Module 14: Device Drivers
Linux classifies devices into three types:
| Type | Interface | Examples |
|---|---|---|
| Character | Byte stream | Terminals, serial ports, /dev/null |
| Block | Random access, buffered | Disks, SSDs (/dev/sda) |
| Network | Packet-based | Ethernet, WiFi |
Driver model:
struct device— Hardware devicestruct driver— Software driverstruct bus_type— Connection (PCI, USB, platform)
Module 15: Kernel Modules
Kernel modules (.ko files) extend the kernel without recompiling:
1#include <linux/module.h> 2#include <linux/kernel.h> 3 4static int __init mymod_init(void) { 5 printk(KERN_INFO "Module loaded\n"); 6 return 0; 7} 8 9static void __exit mymod_exit(void) { 10 printk(KERN_INFO "Module removed\n"); 11} 12 13module_init(mymod_init); 14module_exit(mymod_exit); 15MODULE_LICENSE("GPL");
Commands:
insmod hello.ko— Load modulermmod hello— Remove modulelsmod— List loaded modulesmodprobe— Load with dependency resolutiondmesg— View kernel log
Module 16: Workqueues
Workqueues defer work to process context (where sleeping is allowed):
1#include <linux/workqueue.h> 2 3static void my_work_handler(struct work_struct *work) { 4 // Can sleep here! 5} 6 7static DECLARE_WORK(my_work, my_work_handler); 8 9// Schedule from interrupt context: 10schedule_work(&my_work);
Types:
system_wq— Shared, unboundsystem_highpri_wq— High priorityalloc_workqueue()— Custom queue with attributes
Module 17: Timers
Kernel timers are soft timers — they fire in interrupt context:
1#include <linux/timer.h> 2 3struct timer_list my_timer; 4 5void timer_callback(struct timer_list *t) { 6 printk("Timer fired!\n"); 7} 8 9timer_setup(&my_timer, timer_callback, 0); 10mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000)); // 1 second
High-resolution timers (hrtimers): nanosecond precision for real-time needs.
Module 18: Locks
The kernel runs in a preemptible, multi-CPU, interrupt-driven environment. Locks are essential.
Locking rules:
- Never sleep while holding a spinlock
- Always acquire locks in the same order (prevent deadlocks)
- Keep critical sections short
Module 19: Spinlocks
Spinlocks busy-wait until the lock is available. Used when sleeping is not allowed:
1#include <linux/spinlock.h> 2 3spinlock_t my_lock; 4spin_lock_init(&my_lock); 5 6spin_lock(&my_lock); // Disables preemption on UP, spins on SMP 7// Critical section 8spin_unlock(&my_lock);
Variants:
spin_lock_irqsave()— Save and disable interruptsspin_lock_bh()— Disable bottom halvesread_lock()/write_lock()— Reader-writer spinlocks
Module 20: RCU (Read-Copy-Update)
RCU is a lock-free synchronization mechanism optimized for read-mostly data:
- Readers: No locks, no atomics, just
rcu_read_lock()/rcu_read_unlock() - Writers: Copy the data, modify the copy, then atomically update the pointer
- Grace period: Wait until all pre-existing readers finish
1rcu_read_lock(); 2p = rcu_dereference(global_ptr); // Safe read 3// Use p 4rcu_read_unlock(); 5 6// Writer: 7new_p = kmalloc(sizeof(*new_p), GFP_KERNEL); 8*new_p = *old_p; 9// modify new_p 10rcu_assign_pointer(global_ptr, new_p); 11synchronize_rcu(); // Wait for grace period 12kfree(old_p);
Use cases: Routing tables, process lists, kernel data structures with many readers.
Module 21: Atomic Operations
Kernel atomics are architecture-specific but portable:
1#include <linux/atomic.h> 2 3atomic_t counter = ATOMIC_INIT(0); 4atomic_inc(&counter); 5int val = atomic_read(&counter); 6 7// 64-bit: 8atomic64_t big_counter; 9atomic64_set(&big_counter, 42);
Bit operations:
1set_bit(0, &my_flags); // Atomically set bit 0 2test_and_set_bit(1, &flags); // Test old value, then set
Module 22: Kernel Debugging
Tools and techniques:
| Tool | Purpose |
|---|---|
printk() / pr_debug() | The kernel's printf — check with dmesg |
dynamic debug | Enable/disable pr_debug() at runtime |
BUG() / WARN() | Trigger oops/panic with stack trace |
KDB | In-kernel debugger (break into running kernel) |
KGDB | GDB over serial/network for remote debugging |
ftrace | Function tracer — trace-cmd, kernelshark |
perf | Performance counters and profiling |
kprobes | Dynamic instrumentation — breakpoints in kernel code |
eBPF | Safe, verified programs for tracing and filtering |
Oops analysis: When the kernel crashes, it prints an oops with register state, stack trace, and offending instruction. Decode with scripts/decodecode.
🛠 Hands-On: Kernel Module Progression
The following code files progress from a simple hello module to a custom system call. All code follows modern Linux kernel conventions (tested against 5.x–6.x APIs).
Download complete package: linux_kernel_hands_on.tar.gz
Step 1: hello_kernel.c
A minimal loadable kernel module.
Build: make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
What it demonstrates:
module_init()/module_exit()macrosprintk()for kernel loggingcurrentmacro to access the running processMODULE_LICENSE()and metadata macros
Download: hello_kernel.c | Makefile.hello
Step 2: module_params.c
Passing arguments to a kernel module at load time.
Load: sudo insmod module_params.ko name="CyberSec" count=3 debug=1 ports=80,443,22
What it demonstrates:
module_param()for scalar parametersmodule_param_array()for array parametersMODULE_PARM_DESC()for documentation- Type validation at load time
Download: module_params.c | Makefile.params
Step 3: proc_fs.c
Creating a /proc entry that exposes kernel data to userspace.
Read: cat /proc/kernel_security_status
Write: echo "2" | sudo tee /proc/kernel_security_status
What it demonstrates:
proc_create()andproc_ops(modern kernel API)seq_filefor formatted output- Read and write handlers
copy_from_user()/copy_to_user()for safe userspace access
Download: proc_fs.c | Makefile.proc
Step 4: char_device.c
A complete character device driver with read(), write(), and ioctl().
Auto-creates: /dev/kerndev
Test: echo "test" | sudo tee /dev/kerndev then cat /dev/kerndev
What it demonstrates:
register_chrdev()andclass_create()/device_create()file_operationsstructuremutexfor thread-safe accessioctlfor device-specific commandskmalloc()/kfree()for kernel memory- Userspace test program with
ioctl()calls
Download: char_device.c | Makefile.chardev | userspace_ioctl.c
Step 5: custom_syscall.c
A custom system call implementation with full installation instructions.
What it demonstrates:
SYSCALL_DEFINE2()macro for syscall definition- Adding entries to
syscall_64.tbl - Adding prototypes to
syscalls.h - Kernel recompilation process
- Userspace invocation via
syscall()function
⚠️ Requires kernel source patching and recompilation. See the detailed guide.
Download: custom_syscall.c | userspace_syscall.c | README_SYSCALL.md | Makefile.syscall
Master README
Download the comprehensive setup and build guide: README_KERNEL.md
🎓 Summary: The Kernel Mindset
| Concept | Key Takeaway |
|---|---|
| Source Tree | kernel/, fs/, mm/, net/, drivers/ — know where things live |
| Compilation | .config controls everything; y=built-in, m=module, n=off |
| Boot | BIOS → GRUB → vmlinuz → start_kernel() → PID 1 |
| task_struct | The kernel's representation of every process/thread |
| Scheduler | CFS uses vruntime and red-black trees for fairness |
| fork/exit | copy_process() clones; do_exit() cleans up; init reaps zombies |
| Kernel Threads | Processes with no userspace — kthread_create() |
| Interrupts | Top half (fast) + bottom half (deferred); no sleep in IRQ |
| Syscalls | syscall instruction → sys_call_table[rax] → handler |
"The kernel is the only software that cannot lie. If the kernel is compromised, no user-space tool can be trusted. Understanding the kernel is understanding the root of trust."
Master Linux kernel internals, and you can detect rootkits, write hardened systems, analyze exploits at the source, and build security tools that operate at the highest privilege level.