Linux System Architecture-Free Linux kernel development course

Previous Lecture
Next Lecture

Linux System Architecture
Chapter 4, Part 1 — User Space, Kernel Space, APIs & System Calls
🎓 Beginner Friendly
⚡ Kernel 6.x Updated
🆓 100% Free
💡 Interview Q&A Included

What You Will Learn

Two Privilege Modes
User Space vs Kernel Space
Library APIs
glibc
System Calls
Mode Switch
Process Address Space
Linux API Layers

Before We Begin

If you have ever written a C program that calls printf() or read(), you have already used two different layers of the Linux system without even realising it. One of those belongs to user space and the other crosses the boundary into kernel space.

Understanding this boundary — why it exists, what lives on each side, and how the two sides talk to each other — is the most important foundation for Linux kernel programming. Everything else builds on top of this. Let us walk through it step by step, in plain language.

1. The Big Picture — Two Worlds in One Machine

Every modern operating system splits its world into two zones. Linux is no different. At the hardware level, the CPU itself enforces this split using privilege rings (on x86 hardware, these are Ring 0 through Ring 3). Linux uses only two of them in practice:

CPU Privilege Rings — How Linux Uses Them

Hardware
Platform

USER SPACE (Ring 3)
KERNEL SPACE (Ring 0)

Ring 0 = highest privilege (kernel), Ring 3 = lowest privilege (user apps). Linux skips rings 1 and 2.

Think of it like a building with two security zones. The public area (user space) is where visitors roam freely. The restricted area (kernel space) is where the core systems live — power controls, security, plumbing. Visitors cannot walk in there directly; they must request a service from the front desk (system call interface).

2. User Space — Where Your Programs Live

User space is the environment in which all ordinary programs run — your text editor, web browser, database server, shell scripts, and the applications you write as a developer. Each process running in user space gets its own virtual address space, isolated from every other process.

This isolation is crucial. If your application crashes, it does not take down the entire system. The kernel can simply clean it up and move on. This is one of the biggest lessons from early computing — running everything in one space led to catastrophic failures when any one piece went wrong.

User Space — Multiple Isolated Processes
Process P1
e.g., bash shell
Virtual
Address Space
Process P2
e.g., Python app
Virtual
Address Space
•••
Process Pn
e.g., web server
Virtual
Address Space
⚠️ Each process is completely isolated. A crash in P1 does NOT affect P2 or Pn.

User space is also home to threads, shared libraries, and daemons (background services like systemd, sshd, cron). All of these run at the lower privilege level — they cannot directly touch hardware or kernel data structures.

3. Library APIs — The Building Blocks for User Space

When writing applications, developers rely heavily on Application Programming Interfaces (APIs). Rather than writing everything from scratch, you call pre-built, well-tested functions packaged in libraries.

On Linux, the most important library is glibc — the GNU C Standard Library. Every user-mode application on a Linux system is automatically linked against glibc. You do not choose this; it happens automatically during compilation. glibc provides the C standard library functions that are used in virtually every program.

Library API Layers in User Space

Your Application Code

calls printf(), malloc(), open(), …

Library APIs (glibc and others)

printf(3)  |  malloc(3)  |  strcmp(3)  |  scanf(3)  |  free(3)
Man section 3 — pure user-space code
↓ (some library calls trigger)

System Call Interface

open(2)  |  read(2)  |  write(2)  |  fork(2)  |  socket(2)
Man section 2 — crossing into kernel

Library APIs vs System Call APIs — What is the Difference?

This is a very common point of confusion. Here is a clear way to think about it:

📚 Library APIs (man section 3)
  • Pure user-space code
  • Included from header files
  • No privilege switch needed
  • Examples: printf(), malloc(), strlen(), fopen()
⚙️ System Calls (man section 2)
  • Kernel-level operations
  • Triggers CPU mode switch
  • Only legal gateway into kernel
  • Examples: open(), read(), fork(), socket()

Notice the man page section numbers. When you see printf(3), the (3) tells you it is a library function. When you see open(2), the (2) tells you it is a system call. This notation is used consistently across all Linux man pages and is worth memorising.

💡 Interesting Fact: printf() is a library function, but internally it eventually calls write(2) — a system call — to actually output characters to the terminal. Many library functions are wrappers that eventually reach a system call. The library layer adds convenience, formatting, and buffering on top.

4. System Calls — The Only Legal Gateway to the Kernel

Here is the key question: if user space and kernel space are completely separate — different address spaces, different privilege levels — how does a user application ever do anything useful, like reading a file, creating a process, or sending data over a network?

The answer is system calls. A system call is a special, controlled mechanism that allows a user-space process to request a service from the kernel. It is the only legitimate way for a user process to enter kernel space. Everything else — direct memory access to kernel structures, directly calling kernel functions — is illegal and will cause a crash or a security violation.

How a System Call Works — Step by Step

USER SPACE (Ring 3)
Step 1: App calls read(fd, buf, n)
Step 2: glibc sets up syscall number in register

⚡ CPU Mode Switch: Ring 3 → Ring 0 (via syscall instruction)

KERNEL SPACE (Ring 0)
Step 3: Kernel looks up syscall table, finds sys_read()
Step 4: Kernel executes, copies data to user buffer

↑ Return: Ring 0 → Ring 3, result back in register

Step 5: Application receives return value and continues executing

Why is This Design Important?

This controlled gateway design is what makes Linux both stable and secure. Because every request to the kernel must go through a defined, validated interface:

  • The kernel can validate parameters before acting on them, preventing privilege escalation attacks.
  • A misbehaving user process cannot corrupt kernel data structures.
  • The kernel can apply per-process limits, capabilities, and security policies at the system call boundary.
  • Crashes in user space are contained — only that process dies, not the entire system.

How Many System Calls Does Linux Have?

In modern Linux (kernel 6.x), there are around 450+ system calls on x86-64 architecture. The number varies by architecture. You can look at the master syscall table yourself:

# On an x86-64 Linux system, count the syscalls:
grep -c "^[0-9]" /usr/share/syscalls/x86_64.tbl 2>/dev/null || \
  grep -c "common" /usr/src/linux/arch/x86/entry/syscalls/syscall_64.tbl

# Or simply look at the table:
cat /usr/src/linux/arch/x86/entry/syscalls/syscall_64.tbl | head -40

Some commonly used system calls you will encounter frequently as a kernel programmer:

System Call What It Does Category
open(2) Opens a file, returns a file descriptor File I/O
read(2) Reads data from a file descriptor File I/O
write(2) Writes data to a file descriptor File I/O
fork(2) Creates a new process (child copy of parent) Process
execve(2) Replaces current process image with a new program Process
socket(2) Creates a network socket endpoint Networking
mmap(2) Maps files or memory into process address space Memory
clone(2) Creates child process/thread with fine-grained control Process/Thread
ioctl(2) Device-specific control operations Device Drivers
🔍 Kernel 6.x Note: In modern kernels, the syscall instruction (on x86-64) replaced the older int 0x80 interrupt approach for making system calls. The syscall instruction is faster as it avoids the overhead of going through the interrupt descriptor table. On ARM64, the equivalent instruction is svc #0. If you are studying older material (pre-kernel 3.x era), you may see references to int 0x80 — this still works on 32-bit x86 but is not used on 64-bit systems.

5. Putting It All Together — The Full Architecture

Now that we understand each piece individually, let us look at how everything connects in a real running Linux system.

Complete Linux Architecture — From Application to Hardware

USER SPACE
Process P1
Threads
Libraries
Daemons
Process P2
Threads
Libraries
Daemons
···
Process Pn
Threads
Libraries
Daemons

⚡ SYSTEM CALL INTERFACE (The Only Bridge)

KERNEL SPACE
Core Kernel
Scheduler, IPC
Timers, Signals
Memory Mgmt
VAS, Paging
Slab allocator
VFS / Networking
File systems
TCP/IP stack
Device Drivers
NIC, Storage
GPIO, USB

🖥️ Hardware Platform — CPU, RAM, Storage, Network Interfaces, Peripherals

Every arrow crossing the user-kernel boundary is a system call. There is no other way. This is why kernel programming is both powerful and demanding — the kernel code you write runs in the privileged zone. A bug there can bring down the entire machine, not just one process.

6. Hands-On — Watching System Calls in Action

The best way to understand system calls is to watch them happen. Linux provides a fantastic tool called strace that intercepts and logs every system call a process makes.

# Watch every system call made by the 'ls' command
strace ls

# Count how many times each system call is used (-c flag)
strace -c ls /tmp

# Trace only specific system calls (e.g., only open and read)
strace -e trace=open,read,write ls

# Trace a running process by its PID
strace -p 1234

Try running strace -c ls on your system. You will see output like this (abbreviated):

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 28.45    0.000312          10        30           mmap
 22.10    0.000242           8        29           read
 15.30    0.000168          14        12           openat
  8.90    0.000098           7        14           fstat
  6.20    0.000068           9         7     7      access
  ...
------ ----------- ----------- --------- --------- ----------------
100.00    0.001096                   142     7      total

Even a simple ls command makes over 100 system calls! Most of them are for loading shared libraries, reading directory entries, and writing output to the terminal. This gives you a real appreciation for how much work the kernel does on behalf of even simple programs.

✅ Exercise: Run strace -c /bin/date and note which system call is called the most times. Then compare with strace -c /bin/cat /etc/hostname. Can you see the difference in which syscalls dominate based on what the program does?

📋 Interview Questions & Answers

These are real questions asked in Linux kernel engineer interviews at embedded companies, semiconductor firms, and OS development roles.

Q1. What is the difference between user space and kernel space?
User space is the memory region where user applications run with restricted (unprivileged) access. Each process has its own isolated virtual address space. Kernel space is the memory region where the kernel code runs with full CPU privileges — it can access all hardware, manage memory, and control processes. The separation ensures that a crash in one user process does not affect the kernel or other processes. On x86-64, user space uses CPU Ring 3 and kernel space uses Ring 0.
Q2. What is a system call, and why is it the only legal way to enter kernel space?
A system call is a controlled software interface that allows user-space programs to request services from the kernel. It is the only legal entry point because it goes through a well-defined, validated gate in the kernel — the system call handler. When a user process executes a system call (via the syscall instruction on x86-64), the CPU automatically switches from Ring 3 to Ring 0 and jumps to the kernel’s entry point. The kernel validates arguments before acting, preventing privilege abuse or kernel corruption. Attempting to jump directly to kernel code from user space causes a CPU exception/fault.
Q3. What is glibc and why is every Linux application linked against it?
glibc (GNU C Library) is the standard C library implementation for Linux. It provides the C standard library functions (like printf, malloc, strlen) and also provides wrappers around Linux system calls, making them easier to use from C code. Every user-mode application is automatically linked against glibc because the C runtime startup code (crt0/crt1) which is responsible for setting up the environment before main() is called, is part of glibc. You can use ldd /bin/ls to see this linkage in action.
Q4. What is the difference between a library API and a system call? Give an example.
A library API (man section 3) runs entirely in user space — it does not cause a mode switch. printf() is a classic example; it formats a string in user space memory. A system call (man section 2) causes a CPU privilege switch from user mode to kernel mode. write() is an example; it asks the kernel to actually send bytes to a file descriptor. In practice, printf() internally buffers data and eventually calls write() to flush it — so library functions can and do trigger system calls internally.
Q5. How has the mechanism for making system calls changed from older kernels to modern Linux 6.x?
In very old Linux (32-bit x86), system calls were triggered using the int 0x80 software interrupt. This was slow because it involved going through the Interrupt Descriptor Table (IDT) and performing a full context save. Linux later adopted the sysenter/sysexit instructions (Intel) which were faster. Modern 64-bit Linux (2.6.x onwards, and all current 6.x kernels) uses the syscall and sysret instructions on x86-64, which are the fastest mechanism as they avoid IDT lookup and use dedicated registers (RDI, RSI, RDX, RCX, R8, R9 for arguments; RAX for syscall number and return value). On ARM64, the equivalent is svc #0.
Q6. What tool would you use to debug which system calls a process is making?
strace is the primary tool. It intercepts and prints system calls made by a process and their return values. Use strace -c program for a summary count, strace -e trace=network program to filter by category, or strace -p PID to attach to a running process. For kernel-level tracing with lower overhead, you can use perf trace or ftrace (via /sys/kernel/debug/tracing/). For production systems, eBPF-based tools like bpftrace can trace syscalls with minimal performance impact.
Q7. Can the kernel itself call a system call?
No — system calls are a mechanism for user space to enter the kernel. The kernel itself runs in Ring 0 and does not need to cross the privilege boundary. Kernel code calls internal kernel functions directly. In early Linux (before kernel 5.x), there was a mechanism called sys_call_table that kernel code could technically invoke, but this was considered a hack and has been removed or restricted. Modern kernel code uses in-kernel APIs like VFS functions, memory allocator calls (kmalloc), and so on directly — no system call overhead needed.

What’s Next?

In Part 2 of this chapter, we dive deep into Kernel Space Components — the major subsystems that make up the Linux kernel: the core kernel, memory management, VFS, networking, IPC, and the monolithic architecture explained with real diagrams.

EmbeddedPathashala — Free Linux Kernel Programming | Free Embedded Systems Course | Free Linux Device Drivers Course

Previous Lecture
Next Lecture

Leave a Reply

Your email address will not be published. Required fields are marked *