What The Linux Kernel Does
Free Linux Kernel Development Course — Chapter 4, Lecture 1
free embedded linux course
free linux device drivers course
free embedded systems course
kernel user space kernel space
If you are serious about a free linux kernel development course, the very first thing you need is a mental model of what the kernel actually does for a running system. Every embedded product you will ever bring up — a Wi-Fi router, an automotive infotainment unit, a industrial PLC — hands almost all of its heavy lifting to this one component. In this lecture we build that mental model from scratch, using nothing but a fresh diagram and a tiny original program, before we go anywhere near configuring or building a kernel image in later lectures.
What You Will Learn
User space vs kernel space
System calls and privilege levels
Interrupts and device drivers
GNU/Linux, Android, and embedded userspaces
Inspecting kernel identity from the shell
Prerequisites
You should be comfortable with a Linux shell and basic C. If you have not yet gone through our free embedded systems course chapters on toolchains and bootloaders, it helps but is not required — we will not assume any bootloader knowledge here.
The Kernel Is Not The Operating System
People say “Linux” when they mean an entire operating system, but strictly speaking the kernel is only one piece of it. The kernel alone cannot do anything useful for a user sitting at a terminal — it needs a C library, a set of command-line tools, and usually a init system layered on top before it becomes a usable system.
This separation is exactly what gives Linux its flexibility. Pair the kernel with a full GNU userspace and you get a desktop or server distribution. Pair it with Android’s Bionic C library and its Java-based framework and you get the world’s most widely deployed mobile OS. Pair it with a minimal Busybox userspace and a handful of statically linked binaries and you get the compact root filesystem that ships inside routers, set-top boxes, and industrial gateways. The kernel binary barely changes across these three cases — everything above it does.
Contrast this with BSD-family systems such as FreeBSD, OpenBSD, and NetBSD. There, the kernel, the toolchain, and the base userspace live in a single source tree and are versioned together. Linux deliberately keeps the kernel decoupled from everything else, and that decoupling is a big part of why it scales from a smartwatch to a supercomputer using the same kernel source tree.
| Combination | Kernel | Userspace / C library | Typical use |
|---|---|---|---|
| GNU/Linux | Linux | GNU coreutils + glibc | Desktops, servers |
| Android | Linux (patched) | Bionic + Java framework | Phones, tablets, wearables |
| Buildroot/Yocto embedded image | Linux | BusyBox + musl/glibc | Routers, IoT, industrial boards |
| FreeBSD | BSD kernel | BSD base system | Storage appliances, firewalls |
Three Jobs, One Kernel
Whatever userspace sits on top, every Linux kernel exists to do three things: manage system resources such as CPU time and memory, talk directly to hardware, and expose a stable API so that applications never have to touch that hardware themselves. That third point matters more than people give it credit for — it is the reason the same compiled application can run on wildly different silicon as long as the kernel underneath presents the same system call interface.
| USER SPACE |
| +—————————————+ |
| | Your Application | |
| +—————————————+ |
| | C Library (glibc / musl) | |
| +—————————————+ |
+——————–|———————–+
| trap / software interrupt
| (switches CPU privilege level)
+——————–v———————–+
| KERNEL SPACE |
| +—————————————+ |
| | System Call Dispatcher | |
| +—————————————+ |
| | Scheduler | Filesystem | Networking | |
| +—————————————+ |
| | Device Drivers | |
| +—————————————+ |
+——————–|———————–+
| reads / writes registers
+——————–v———————–+
| HARDWARE |
| (raises interrupts back up) |
+——————————————-+
Applications run at a low CPU privilege level and cannot touch hardware registers directly — they can only call into the C library. When that library needs a kernel service (opening a file, allocating memory, sending a network packet) it issues a system call, which is a controlled, architecture-specific trap that raises the CPU’s privilege level just long enough to run trusted kernel code. Once inside, the system call dispatcher routes the request to the right subsystem: scheduling calls go to the scheduler, file operations go to the filesystem layer, and anything that needs the actual silicon eventually reaches a device driver.
The path can also run the other way. Hardware does not wait to be asked — it raises an interrupt whenever it has something urgent to report, a packet arriving, a button press, a DMA transfer finishing. Interrupts can only be serviced by kernel code; a userspace application has no mechanism to receive one directly. This is precisely why every peripheral on an embedded board needs a kernel-side driver even if the application logic that consumes its data lives entirely in userspace.
Seeing The Kernel From Userspace
You do not need to write a driver to observe this boundary — a single system call is enough. The example below queries kernel identity information using the raw uname() system call rather than any wrapper, so you can see the user space to kernel space hop explicitly.
/* ep_kernel_id.c - queries kernel identity via a direct system call */
#include <stdio.h>
#include <sys/utsname.h>
int main(void)
{
struct utsname info;
/* uname() traps into the kernel; the kernel fills "info" and returns */
if (uname(&info) != 0) {
perror("uname");
return 1;
}
printf("Kernel name : %s\n", info.sysname);
printf("Kernel release : %s\n", info.release);
printf("Kernel version : %s\n", info.version);
printf("Machine arch : %s\n", info.machine);
return 0;
}
$ gcc -o ep_kernel_id ep_kernel_id.c
$ ./ep_kernel_id
Kernel name : Linux
Kernel release : 6.9.0-generic
Kernel version : #1 SMP PREEMPT_DYNAMIC
Machine arch : x86_64
Every field in that output was filled in by kernel code, not by your program. Your call to uname() compiled down to a single instruction that trapped into the kernel, the kernel copied data from its own internal structures into your process’s memory, and control returned to userspace — all in a few microseconds, and all invisible unless you go looking for it with a tool like strace.
$ strace -e trace=uname ./ep_kernel_id
uname({sysname="Linux", nodename="ep-board", ...}) = 0
Real-World Use Case
On an embedded board, this boundary is why bring-up work is split cleanly in two. Application engineers write userspace code against POSIX and library APIs and rarely care which SoC they are running on. Kernel and driver engineers own everything below the system call line — clock trees, interrupt controllers, DMA engines — and their job is to make sure the same userspace application “just works” whether it is running on an NXP i.MX board or a Rockchip one, because both expose the identical kernel API upward.
Common Mistakes
- Assuming a device driver can run “a bit slower” in userspace via
/dev/memmmap tricks instead of writing a real driver — this breaks interrupt handling and is unsafe on production hardware. - Confusing “Linux” the kernel with “Linux” the distribution when debugging — a segfault in
bashis a userspace bug, not a kernel bug, even though people casually blame “Linux.” - Forgetting that interrupts cannot be handled by a normal user process — polling a GPIO in a tight userspace loop instead of registering an interrupt handler wastes CPU cycles the scheduler could give to other work.
Best Practices
- When debugging “why is my app slow,” check whether you are userspace-bound or kernel-bound first with
strace -cbefore touching driver code. - Keep hardware-specific logic in the kernel driver and keep policy (what to do with the data) in userspace — this is the long-standing Linux design philosophy and it keeps drivers portable.
Security Consideration
The user space / kernel space split is also Linux’s primary security boundary. Every privilege-escalation exploit that matters ultimately finds a way to trick kernel code — often a device driver — into doing something on the attacker’s behalf while running at the elevated privilege level. This is why driver code review is held to a stricter standard than application code: a bug in your app crashes your app, but a bug in a driver can compromise the whole system.
Summary
The kernel is not the whole operating system — it is the trusted core that manages resources, talks to hardware, and exposes a stable API, while GNU, Android, or BusyBox userspaces sit on top and give it a personality. Every interaction between an application and hardware crosses the user space / kernel space boundary through a system call, and every unsolicited event from hardware crosses back through an interrupt. With that model in place, the next lecture moves on to actually choosing which kernel version to build for your board.
FAQ
Is Linux an operating system or a kernel?
Linux itself is only the kernel. “Linux” the operating system you install is really GNU/Linux, Android, or a BusyBox-based embedded image — the kernel paired with a userspace.
What is the difference between user space and kernel space?
User space runs applications at a restricted CPU privilege level; kernel space runs trusted code that can access all memory and hardware registers. Applications reach kernel space only through system calls.
Why can’t a userspace program handle a hardware interrupt directly?
Interrupts are delivered to whichever privilege level the CPU is configured to trap into, which is kernel space. Only kernel code — typically a device driver — can register and service an interrupt.
What is a system call?
A system call is a controlled request from userspace into the kernel, triggered by a CPU trap or software interrupt, that lets an application ask for a kernel service such as file I/O or memory allocation.
Why does embedded Linux use BusyBox instead of full GNU userspace?
BusyBox bundles dozens of common Unix tools into one small statically-linkable binary, which drastically reduces root filesystem size — important on embedded storage that may only have tens of megabytes to spare.
Do all Linux-based systems use the same kernel?
They use the same kernel source tree, though vendors and Android carry patches on top. The userspace above the kernel (GNU, Android’s Bionic/Java stack, or BusyBox) is what actually differs.
Is this course really free?
Yes — this is part of EmbeddedPathashala’s free linux kernel development course and free embedded systems course, published chapter by chapter with no paywall.
Continue The Free Linux Kernel Development Course
Next up: how to choose a kernel version and understand the mainline release cycle.
