Introduction to the Linux Kernel

Advanced Linux Development Course · Module 1 · Lecture 5
Introduction to the Linux Kernel
Architecture · User Space vs Kernel Space · System Calls · Kernel Subsystems · Driver Types
9
Key Concepts
3
Driver Types
5
Kernel Subsystems
Free
Course

🏷️ Keywords
Linux Kernel User Space Kernel Space System Call Interface Process Management Memory Management VFS Device Drivers Character Driver Block Driver Network Driver Major Minor Numbers GNU C Kernel Programming

Welcome to Lecture 5 of the Advanced Linux Development Course on EmbeddedPathashala. In this lecture we build the foundation for everything that follows: we look at what the Linux kernel actually is, understand the critical separation between user space and kernel space, learn how applications cross that boundary through the system call interface, explore the five major kernel subsystems, discover why kernel programming is fundamentally different from normal C, and finally classify the three types of Linux device drivers along with the major/minor device number system.

1. What is the Linux Kernel?

The Linux kernel is the core software layer that sits between your hardware and all the user applications running on the system. It is responsible for managing every piece of hardware — CPU, RAM, disks, network cards, USB devices — and providing a clean, secure interface to the programs running on top.

💡 Simple Analogy

Think of the Linux kernel as the manager of a large restaurant. The customers are users. The waitstaff are applications (your shell, browser, vim). The kitchen with all its equipment is the hardware. The staff takes orders from customers but never touches the kitchen equipment directly — only the manager (kernel) is allowed back there. This separation keeps the entire restaurant running safely and efficiently.

🔒 Security Boundary

Separates trusted kernel code from untrusted user programs. A crash in user space kills only that program. A crash in the kernel can bring down the entire system.

⚙️ Resource Manager

Decides which process gets CPU time, how much memory each program can use, and who gets to read or write to a device at any given moment.

🔌 Hardware Abstraction

Hides the differences between a Raspberry Pi and an x86 server behind a consistent API. Your C application calls open() the same way regardless of the hardware.

2. Linux Architecture: User Space vs Kernel Space

Every Linux system is divided into two fundamental privilege levels. The CPU hardware itself enforces this boundary through CPU privilege rings (Ring 0 for kernel, Ring 3 for user applications on x86 architecture).

Linux Architecture Stack — Full View
LINUX SYSTEM ARCHITECTURE ================================
┌─────────────────────────────────────────────────────────────────────────┐
│ USER SPACE (Ring 3) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ bash │ │ vim │ │ gcc │ │ browser │ │ your │ │
│ │ shell │ │ editor │ │ compiler │ │ │ │ app │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└──────────────────────────┬──────────────────────────────────────────────┘
System Calls (open, read, write, fork …)
═══════════════════════════╪══════════════════════════════════════════════
╔══════════════════════════╪══════════════════════════════════════════════╗
║ KERNEL SPACE (Ring 0) — Privileged Mode ║
║ ║
║ ┌─────────────────────────────────────────────────────────────────┐ ║
║ │ SYSTEM CALL INTERFACE (SCI) │ ║
║ │ Validates requests, switches CPU to privileged mode, dispatches│ ║
║ └───────────────────────────┬─────────────────────────────────────┘ ║
║ │ ║
║ ┌────────────┐ ┌─────────────┐ ┌──────────────┐ ┌───────────────┐ ║
║ │ Process │ │ Memory │ │ Virtual │ │ Network │ ║
║ │ Management │ │ Management │ │ File System │ │ Subsystem │ ║
║ └────────────┘ └─────────────┘ └──────────────┘ └───────────────┘ ║
║ ┌──────────────────────────────────────────────────────────────────┐ ║
║ │ DEVICE DRIVERS │ ║
║ │ char drivers | block drivers | network drivers │ ║
║ └──────────────────────────────────────────────────────────────────┘ ║
╚══════════════════════════════════════════════════════════════════════════╝
════════════════════════════╪══════════════════════════════════════════════
┌──────────────────────────────────────────────────────────────────────────┐
│ HARDWARE │
│ CPU RAM Disk/SSD NIC Console/TTY │
└──────────────────────────────────────────────────────────────────────────┘
🟦 User Space
  • All applications you run (bash, vim, your programs)
  • Runs in restricted CPU mode (Ring 3 on x86)
  • Cannot access hardware directly
  • Uses virtual memory — each process thinks it has the whole address space
  • A crash here kills only that process (segmentation fault)
🟥 Kernel Space
  • The kernel and all device drivers run here
  • Full CPU privilege — can access any memory or hardware
  • Single shared address space for the entire kernel
  • A crash here causes a kernel panic — entire system halts
  • No libc, no printf, no malloc — uses kernel-internal APIs
🔑 Key Insight

The CPU hardware enforces the user/kernel boundary. When your program tries to access kernel memory or hardware registers directly, the CPU raises a fault — the kernel terminates your program. This is why you cannot simply dereference a kernel pointer from a user-space application. The only legitimate crossing point is the System Call Interface.

Kernel Subsystem ↔ Hardware Mapping

Which Subsystem Talks to Which Hardware
KERNEL SUBSYSTEM HARDWARE RESOURCE ───────────────── ─────────────────────────────────────
Process Management ─────► CPU
(schedules which process runs on which core)
Memory Management ─────► RAM
(physical page allocation, virtual memory maps)
Virtual File System ─────► Disk / SSD / NVMe
(read/write in blocks)
Device Control ─────► Console / TTY / GPIO / Sensors
(char/block device nodes)
Networking ─────► Network Interface Cards
(packets, TCP/IP stack)

3. System Call Interface — The Bridge

The System Call Interface (SCI) is the only official, controlled gateway between user space and kernel space. When a program needs something from the kernel — opening a file, allocating memory, sending a network packet — it invokes a system call. This triggers a CPU mode switch from Ring 3 (user) to Ring 0 (kernel).

📞 Simple Analogy

Imagine you are staying in a hotel. You need fresh towels. You cannot walk into the laundry room yourself — it is a restricted staff area. Instead, you call the front desk. The front desk (SCI) takes your request, validates it, and sends the staff (kernel code) to handle it. You never get direct access to the back area, but you get your towels. That is exactly how system calls work.

What happens step-by-step when your app calls open()?

  • App issues the call: Your program calls open(“/dev/tty0”, O_RDWR). This is a C library wrapper (glibc) that prepares the syscall number and arguments in CPU registers.
  • Trap / Interrupt: The syscall instruction (x86-64) is executed. The CPU saves user-space register state and switches to Ring 0 — privileged kernel mode.
  • Kernel dispatcher: The kernel looks up the syscall number (e.g., __NR_open = 2 on x86-64) in the system call table and jumps to sys_open().
  • Validation: The kernel validates the filename pointer (is it in user-space memory?), checks file permissions, and verifies the calling process has the right capabilities.
  • Driver interaction: The kernel opens the file/device node, possibly invoking the corresponding driver’s open() function (for a device like /dev/tty0).
  • Return: A file descriptor integer (e.g., 3) is placed in the return register. The CPU switches back to Ring 3 (user mode). Your program resumes with the file descriptor.
System Call Flow Diagram
USER SPACE                                KERNEL SPACE
─────────────────────────────────────────────────────────────────────

Your Program                              Kernel

┌─────────────────┐        ┌──────────────────────────┐
│ fd = open(      │        │ sys_open()              │
│ "/dev/tty0",    │──────► │ • check permissions     │
│ O_RDWR)         │ syscall│ • resolve path via VFS  │
└────────┬────────┘        │ • call driver open()    │
         │                 │ • allocate file object  │
         │ ◄────────────── │ • return fd = 3         │
         ▼                 └──────────────────────────┘

       fd = 3 ✓


═══════════════════════════════════════════════════════════════════════

USER MODE (Ring 3)               │     KERNEL MODE (Ring 0)

No direct hardware access        │     Full privileged hardware access

═══════════════════════════════════════════════════════════════════════

Common System Calls by Category

System Calls Reference
┌────────────────────┬───────────────────────────────────────────────────────┐ │ Category │ Important System Calls │ ├────────────────────┼───────────────────────────────────────────────────────┤ │ File I/O │ open() read() write() close() │ │ │ lseek() stat() │ ├────────────────────┼───────────────────────────────────────────────────────┤ │ Processes │ fork() exec() wait() exit() │ │ │ getpid() kill() │ ├────────────────────┼───────────────────────────────────────────────────────┤ │ Memory │ mmap() munmap() brk() │ │ │ mprotect() mlock() │ ├────────────────────┼───────────────────────────────────────────────────────┤ │ Networking │ socket() bind() connect() │ │ │ send() recv() listen() │ ├────────────────────┼───────────────────────────────────────────────────────┤ │ Directories │ mkdir() rmdir() opendir() │ │ │ readdir() chdir() │ ├────────────────────┼───────────────────────────────────────────────────────┤ │ Users / Permissions│ getuid() setuid() chmod() │ │ │ chown() getgroups() │ ├────────────────────┼───────────────────────────────────────────────────────┤ │ Time │ time() gettimeofday() │ │ │ nanosleep() clock_gettime() │ └────────────────────┴───────────────────────────────────────────────────────┘
🛠️ Try it yourself

You can trace all system calls made by any program using strace:

strace ls # see every syscall ls makes strace -e open ls # filter: show only open() calls strace -c ls # summary: count of each syscall

4. The 5 Major Kernel Subsystems

The Linux kernel is not a monolithic blob of code — it is organized into well-defined subsystems, each responsible for a specific area of the system. Understanding these subsystems is essential before you write any kernel or driver code.

Subsystem 1
🖥️ Process Management

Process management handles the entire lifecycle of a process — creation, scheduling, execution, and termination. It also manages threads (lightweight processes) and inter-process communication (IPC).

Key responsibilities:

  • Creating processes with fork() / exec()
  • CPU scheduling (CFS — Completely Fair Scheduler)
  • Context switching between processes
  • Managing process priority (nice, real-time priorities)
  • Signals (SIGKILL, SIGTERM, SIGINT)
  • IPC: pipes, shared memory, message queues, semaphores

Simple Example:

/* When you type: gcc hello.c -o hello */ fork() ← bash creates a child process exec() ← child replaces itself with gcc wait() ← bash waits for gcc to finish exit(0) ← gcc exits after compilation ← bash gets control back
CFS Scheduler fork() / exec() Context Switch Signals IPC task_struct

Subsystem 2
🧠 Memory Management

Memory management is responsible for allocating and freeing physical RAM, implementing virtual memory (so every process gets its own address space), and handling page faults when a process accesses memory that is not yet loaded.

Key concepts:

  • Virtual memory: each process sees 0x0 to 0xFFFF… as its private address space
  • Paging: memory divided into 4 KB pages, mapped via page tables
  • Page fault: kernel loads page from disk if not in RAM
  • Slab allocator: efficient kernel-internal memory allocation (kmalloc)
  • Swap: overflow RAM pages moved to disk

User vs Kernel allocation:

/* User space */ malloc(size) → calls brk() or mmap() syscall → kernel allocates virtual pages /* Kernel space */ kmalloc(size, GFP_KERNEL) → slab allocator vmalloc(size) → virtually contiguous get_free_pages(GFP_KERNEL, order) → raw pages
Virtual Memory Paging Page Tables kmalloc Slab Allocator OOM Killer

Subsystem 3
📁 Virtual File System (VFS)

The Virtual File System is an abstraction layer that gives every program a single, unified interface to access all types of storage, regardless of the underlying filesystem. Whether it is ext4, FAT32, NFS, or even the virtual /proc and /sys filesystems — your application uses the same open() / read() / write() calls.

VFS — “One Interface, Many Filesystems”
Application:
    open("/proc/cpuinfo")
    open("/mnt/usb/file.txt")
    open("/etc/hosts")

            │                         │                         │
────────────┼─────────────────────────┼─────────────────────────┤
            ▼                         ▼                         ▼

┌───────────────────────────────────────────────────────────────┐
│                  Virtual File System (VFS)                   │
│   unified interface: inode, dentry, file, super_block       │
└───────────────────────────────────────────────────────────────┘
            │                         │                         │
      ┌─────┘                   ┌─────┘                   ┌─────┘
      ▼                         ▼                         ▼

  procfs driver             FAT32 driver               ext4 driver
  (in-memory)             (USB flash drive)         (/dev/nvme0n1p2)

ext4 / FAT32 / NFS inode /proc filesystem /sys filesystem dentry cache page cache

Subsystem 4
🔌 Device Control

Device control manages all hardware devices through device drivers. It provides the interface between the abstract operations in the kernel (open, read, write, ioctl) and the actual hardware registers and DMA transfers. Character and block devices are exposed to user space via device nodes in /dev.

char drivers block drivers /dev nodes ioctl udev / devtmpfs DMA

Subsystem 5
🌐 Networking

The networking subsystem implements the entire TCP/IP stack inside the kernel. It handles packet reception from the NIC driver, routing, TCP/UDP processing, and delivery to sockets in user space.

Layers handled by the kernel:

  • Network driver (NIC) → receives raw frames
  • Layer 2: Ethernet (MAC addresses, ARP)
  • Layer 3: IP routing (source → destination)
  • Layer 4: TCP/UDP (ports, reliability, flow control)
  • Socket layer: delivers data to user-space app

Socket example:

/* Browser loading a webpage */ socket(AF_INET, SOCK_STREAM, 0) → create TCP socket connect(fd, &server_addr, …) → TCP 3-way handshake send(fd, “GET / HTTP/1.1\r\n”, …) → HTTP request recv(fd, buffer, sizeof(buf), 0) → receive response close(fd) → TCP teardown
TCP/IP Stack Sockets Netfilter / iptables sk_buff Routing Table

5. Kernel Programming ≠ Regular C Programming

If you have written C programs before, you might assume kernel programming is just more of the same. It is not. The kernel environment has strict constraints that do not exist in user space. Violating these constraints causes kernel panics or subtle memory corruption bugs. Let us go through each one carefully.

⚠️ The Big Picture

The kernel uses GNU C — the C language as extended by GCC, not standard ISO C. GCC extensions like __attribute__((packed)), typeof(), inline assembly, and likely() / unlikely() are used extensively throughout the kernel source. More importantly, the kernel has no access to the standard C library (libc) — there is no printf(), no malloc(), no <stdio.h>. The kernel provides its own equivalents.

Difference 1
🚫 No Standard C Library Headers

In user-space C, you #include <stdio.h> and call printf(). In the kernel, these headers and functions do not exist. The kernel ships its own set of utility libraries in lib/ of the kernel source tree. You use kernel-specific headers from include/linux/. Even a single missing header will cause a compile error — the kernel build system treats all warnings as errors with -Werror.

❌ User Space (wrong for kernel)

#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char *buf = malloc(128); printf(“Hello %s\n”, buf); free(buf); }

✅ Kernel Space (correct)

#include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> static int __init my_init(void) { char *buf = kmalloc(128, GFP_KERNEL); pr_info(“Hello %s\n”, buf); kfree(buf); return 0; }

Difference 2
💀 No Memory Protection

In user space, if you dereference a NULL pointer or access invalid memory, the kernel sends your process a SIGSEGV signal — the segmentation fault. Only your process dies. The rest of the system is unaffected.

In kernel space, the same bad pointer causes a KERNEL PANIC. The entire system halts (or reboots). There is no safety net. This is why kernel code must be extraordinarily careful about pointer validity.

❌ User Space result: segfault (only your app dies)

int *p = NULL; *p = 42; // → SIGSEGV // only this process dies // system keeps running

❌ Kernel Space result: KERNEL PANIC 💥

int *p = NULL; *p = 42; // → Oops: NULL pointer dereference // BUG: unable to handle kernel NULL pointer // entire system crashes or reboots

Golden Rule for Kernel Code

Always validate pointers. Always check return values. Always use kernel APIs like copy_from_user() when reading data from user space (never dereference a user pointer directly in kernel context).

Difference 3
🔢 No Floating-Point Operations

When the CPU enters kernel mode, the FPU (Floating Point Unit) state is not automatically saved. The kernel explicitly avoids the overhead of saving/restoring FPU registers on every context switch. If you use float or double in kernel code, you will corrupt the floating-point state of whatever user process was running.

❌ Wrong — floating point in kernel

float percentage; percentage = (float)used / (float)total * 100.0; // NEVER do this in kernel code! // Corrupts FPU state of user processes

✅ Correct — integer arithmetic only

unsigned long percentage; percentage = (used * 100) / total; // Scale up by 100 first, then divide // Pure integer math — safe in kernel

Difference 4
📏 Very Small, Fixed Kernel Stack

A user-space process typically has an 8 MB stack (expandable via ulimit). The kernel gives each thread (or CPU, in interrupt context) a stack of only 4–8 KB — and this is fixed, it cannot grow. Declaring large local arrays on the stack will silently overflow it, causing hard-to-debug memory corruption.

❌ Stack overflow risk

static irqreturn_t my_irq_handler(…) { char buffer[65536]; // 64 KB on a 8 KB stack! char temp[8192]; // still risky // → silent stack overflow → corruption }

✅ Use dynamic allocation

static irqreturn_t my_irq_handler(…) { char *buffer; buffer = kmalloc(65536, GFP_ATOMIC); // heap if (!buffer) return IRQ_NONE; // use buffer … kfree(buffer); return IRQ_HANDLED; }

Difference 5
🔄 Asynchronous Interrupts, Preemption & SMP

The kernel can be interrupted at any time by a hardware interrupt. On a multi-core system (SMP — Symmetric Multi-Processing), two CPUs can execute kernel code simultaneously. This makes concurrency and synchronization a major concern that simply does not exist in typical single-threaded user programs.

Synchronization primitives in the kernel:

  • spinlock_t — busy-wait lock (safe in interrupt context)
  • mutex — sleep-capable lock (not in interrupt context)
  • rwlock_t — reader/writer lock
  • atomic_t — atomic integer operations
  • rcu — Read-Copy-Update (high-performance)
/* Wrong — race condition */ static int counter = 0; counter++; // NOT atomic! On SMP: // CPU0: read counter=5 // CPU1: read counter=5 // CPU0: write 6 // CPU1: write 6 ← lost increment! /* Correct — atomic operation */ static atomic_t counter = ATOMIC_INIT(0); atomic_inc(&counter); // Always safe on SMP
Spinlocks Mutexes Atomic operations RCU Interrupt context SMP safety

6. Classification of Device Drivers

Linux categorizes all device drivers into three fundamental types based on how data is transferred between the kernel and the hardware. Understanding this classification tells you immediately how to write the driver and whether a /dev node will be created.

⌨️ Character Driver
/dev node: ✅ YES

Data access: Byte-by-byte stream (like a serial cable — sequential, no random access)

Kernel ID: First character in ls -l is c

Real examples:

  • Keyboard (/dev/input/event0)
  • Serial port / UART (/dev/ttyS0)
  • Terminal (/dev/tty0, /dev/pts/0)
  • Sensors, GPIO, custom embedded devices
  • Sound card (/dev/dsp)
💾 Block Driver
/dev node: ✅ YES

Data access: Fixed-size blocks (512 B or 4096 B) with full random access — you can seek to any block

Kernel ID: First character in ls -l is b

Real examples:

  • SATA hard disk (/dev/sda)
  • NVMe SSD (/dev/nvme0n1)
  • USB flash drive (/dev/sdb)
  • eMMC (embedded flash, /dev/mmcblk0)
  • RAM disk (/dev/ram0)
🌐 Network Driver
/dev node: ❌ NO

Data access: Sends and receives packets. Accessed only through the socket API — never through /dev

Kernel ID: Does NOT appear in ls /dev — use ip link or ifconfig

Real examples:

  • Ethernet NIC (eth0)
  • WiFi adapter (wlan0)
  • Bluetooth HCI (hci0)
  • Virtual loopback (lo)
  • VPN tunnel (tun0, wg0)

🤔 Why don’t network drivers have a /dev node?

Character and block drivers expose a file-like interface — you can open(), read(), and write() them. Network devices transmit packets, not bytes or blocks. The operating model is fundamentally different: you send structured network frames with headers, you deal with connection state, timeouts, and retransmission. The kernel provides the socket API (socket(), send(), recv()) as the correct abstraction for network communication. The interface name (eth0, wlan0) is managed by the kernel’s networking subsystem, not by /dev.

Identifying Driver Types with ls -l /dev
$ ls -l /dev

# ─── CHARACTER DRIVERS (first column = 'c') ───────────────────────────────

crw--w---- 1 root tty   4,   8  May 1 20:58 tty8
    ← major=4, minor=8

crw--w---- 1 root tty   4,   9  May 1 20:58 tty9
    ← major=4, minor=9

crw------- 1 root root  5,   3  May 1 20:58 ttyprintk

crw-rw-rw- 1 root root  1,   5  May 1 20:58 zero
    ← major=1 (memory devices)

crw-rw-rw- 1 root root  1,   3  May 1 20:58 null


# ─── BLOCK DRIVERS (first column = 'b') ───────────────────────────────────

brw-rw---- 1 root disk  8,   0  May 1 20:58 sda
    ← major=8 (SCSI/SATA)

brw-rw---- 1 root disk  8,   1  May 1 20:58 sda1
    ← minor=1 (partition 1)

brw-rw---- 1 root disk 259,  0  May 1 20:58 nvme0n1
    ← major=259 (NVMe)

brw-rw---- 1 root disk 259,  1  May 1 20:58 nvme0n1p1

brw-rw---- 1 root disk 259,  2  May 1 20:58 nvme0n1p2


# ─── NETWORK DRIVERS — NOT in /dev ────────────────────────────────────────

$ ip link show

2: eth0:  <BROADCAST,MULTICAST,UP> mtu 1500 ...
    ← no /dev node!

3: wlan0: <BROADCAST,MULTICAST> mtu 1500 ...

4: hci0:  <BROADCAST,MULTICAST> mtu 1500 ...
    ← Bluetooth — no /dev!

7. Major and Minor Device Numbers

Every character and block device file in /dev is identified by two numbers: the major number and the minor number. Together, they form a unique device identifier that tells the kernel exactly which driver to call and which specific device instance to use.

🏢 Simple Analogy

Think of it like a large apartment complex. The major number is the building name or number — it identifies which driver handles this type of device (e.g., Building 4 = TTY driver, Building 8 = SCSI disk driver). The minor number is the unit or apartment number within that building — it identifies which specific device instance (e.g., Apartment 0 = first disk, Apartment 1 = second disk).

Major # Driver Type Devices
1 Memory devices char /dev/mem, /dev/null, /dev/zero, /dev/random
4 TTY driver char /dev/tty0 … /dev/tty63 (minor = TTY number)
5 Alternate TTY char /dev/tty (current), /dev/console, /dev/ttyprintk
8 SCSI/SATA disk block /dev/sda (minor 0), /dev/sda1 (minor 1) …
259 NVMe block block /dev/nvme0n1 (minor 0), p1 (minor 1) …
136–143 Pseudo-terminals char /dev/pts/0, /dev/pts/1 … (SSH terminals)
180 USB devices char /dev/usb/lp0, /dev/usb/hiddev0 …
Reading a Device File Entry
┌────────────────────────────────────────────────────────────────────────────┐
│ UNDERSTANDING LINUX DEVICE NODES                                          │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│ $ ls -l /dev/tty8                                                          │
│                                                                            │
│ crw--w---- 1 root tty 4, 8 May 1 20:58 tty8                               │
│                                                                            │
│ ├── c = character device                                                   │
│ ├── owner = root                                                           │
│ ├── group = tty                                                            │
│ ├── major number = 4                                                       │
│ │   └── identifies the TTY character driver                                │
│ └── minor number = 8                                                       │
│     └── specific device instance → tty8                                    │
│                                                                            │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│ $ ls -l /dev/nvme0n1p2                                                     │
│                                                                            │
│ brw-rw---- 1 root disk 259, 2 May 1 20:58 nvme0n1p2                       │
│                                                                            │
│ ├── b = block device                                                       │
│ ├── major number = 259                                                     │
│ │   └── identifies the NVMe block driver                                   │
│ └── minor number = 2                                                       │
│     └── partition 2 on this NVMe storage device                            │
│                                                                            │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│ Key Idea:                                                                  │
│                                                                            │
│ major number → selects the device driver                                   │
│ minor number → selects a specific device handled by that driver            │
│                                                                            │
│ Example Flow:                                                              │
│                                                                            │
│ open("/dev/tty8")                                                          │
│          ↓                                                                 │
│ Kernel reads major = 4                                                     │
│          ↓                                                                 │
│ TTY driver selected                                                        │
│          ↓                                                                 │
│ minor = 8 identifies tty8                                                  │
│                                                                            │
└────────────────────────────────────────────────────────────────────────────┘

8. How an Application Accesses a Device

Putting it all together — here is the complete path from an application calling open(“/dev/tty8”, …) all the way to the hardware:

Full Device Access Path
┌────────────────────────────────────────────────────────────────────────────┐
│ OPEN("/dev/tty8") — COMPLETE FLOW                                         │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│ APPLICATION                                                                │
│                                                                            │
│ fd = open("/dev/tty8", O_RDWR);                                            │
│                                                                            │
│                ▼                                                           │
│                                                                            │
│ System Call                                                                │
│ (CPU switches from Ring 3 → Ring 0)                                        │
│                                                                            │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│ SYSTEM CALL INTERFACE                                                      │
│                                                                            │
│ sys_open()                                                                 │
│                                                                            │
│ └── forwards request to VFS                                                │
│                                                                            │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│ VIRTUAL FILE SYSTEM (VFS)                                                  │
│                                                                            │
│ • Resolve pathname "/dev/tty8"                                             │
│ • Locate inode for tty8                                                    │
│ • Read device numbers from inode                                           │
│                                                                            │
│     major = 4                                                              │
│     minor = 8                                                              │
│                                                                            │
│ • Lookup major number in character-device table                            │
│                                                                            │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│ CHARACTER DEVICE TABLE                                                     │
│                                                                            │
│ major 4 ─────► tty_fops                                                    │
│                                                                            │
│ tty_fops = file_operations structure                                       │
│                                                                            │
│ VFS invokes:                                                               │
│                                                                            │
│ tty_fops.open(inode, filp)                                                 │
│                                                                            │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│ TTY DRIVER                                                                 │
│ (drivers/tty/tty_io.c)                                                     │
│                                                                            │
│ • Use minor = 8 to locate tty8 device                                      │
│ • Initialize terminal state                                                │
│ • Configure line discipline                                                │
│ • Enable UART / console hardware                                           │
│                                                                            │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│ HARDWARE                                                                   │
│                                                                            │
│ UART controller / console TTY                                              │
│                                                                            │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│ Kernel returns file descriptor to application                              │
│                                                                            │
│ return fd = 3                                                              │
│                                                                            │
│ APPLICATION                                                                │
│                                                                            │
│ read(fd, ...)                                                              │
│ write(fd, ...)                                                             │
│                                                                            │
│ All future operations use fd = 3                                           │
│                                                                            │
└────────────────────────────────────────────────────────────────────────────┘
✅ Summary: The Complete Data Flow

App → System Call Interface → VFS → /dev node (major, minor) → Device Driver → Hardware

For network drivers, this path is different: App → Socket API → TCP/IP Stack → Network Driver → NIC Hardware
(no /dev node involved)

9. What’s Coming Next in This Course

Now that you have a clear mental model of the Linux kernel architecture, we can go deeper. Here is what the upcoming lectures will cover:

Lecture 6
Linux Kernel Versioning

Understanding version numbers (6.x.y), LTS vs mainline vs stable kernels, and how to choose the right kernel for embedded projects.

uname -rLTS KernelsMainline
Lecture 7
Linux Kernel Source Tree

Tour of the kernel directory structure: arch/, drivers/, fs/, include/, kernel/, mm/, net/. Where to find what.

kernel.orgDirectory LayoutKconfig
Lecture 8
Configuring the Kernel

Using make menuconfig, the .config file, and the Kconfig system. Enabling/disabling drivers. Building a minimal embedded kernel.

menuconfig.configKconfig
Lecture 9
Compiling the Kernel

Building from source: make, vmlinuz, initrd, kernel modules (.ko files), cross-compilation for ARM.

vmlinuzinitrdmake modules
Lecture 10
Boot Sequence

Step-by-step: BIOS/UEFI → GRUB bootloader → kernel decompression → init process → systemd → user login.

GRUBinitramfssystemd
Lecture 11
Root Filesystem & Advanced Topics

Root filesystem structure, building a minimal RootFS with BusyBox, initramfs, and writing your first kernel module.

BusyBoxinitramfsFirst Module

📝 Quick Revision Summary

Everything from This Lecture at a Glance
┌────────────────────────────────────────────────────────────────────────────┐
│ LECTURE 5 : QUICK REVISION                                                │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│ 1. Linux Kernel                                                            │
│                                                                            │
│ The Linux kernel is the core software layer between hardware               │
│ and user-space applications.                                               │
│                                                                            │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│ 2. CPU Privilege Levels                                                    │
│                                                                            │
│ USER SPACE (Ring 3)                                                        │
│ → Applications run here                                                    │
│ → No direct hardware access                                                │
│                                                                            │
│ KERNEL SPACE (Ring 0)                                                      │
│ → Kernel and drivers run here                                              │
│ → Full privileged hardware access                                          │
│                                                                            │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│ 3. System Call Interface (SCI)                                             │
│                                                                            │
│ The SCI is the ONLY legal entry point into the kernel.                     │
│                                                                            │
│ Common system calls:                                                       │
│                                                                            │
│ open()  read()  write()  fork()  socket()                                  │
│                                                                            │
│ Use strace to observe system calls of any Linux program.                   │
│                                                                            │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│ 4. Major Kernel Subsystems                                                 │
│                                                                            │
│ Process Management     ↔ CPU scheduling                                    │
│ Memory Management      ↔ RAM allocation / virtual memory                   │
│ Virtual File System    ↔ disks / SSD / NVMe                                │
│ Device Control         ↔ consoles / peripherals                            │
│ Networking             ↔ network interface cards                           │
│                                                                            │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│ 5. Kernel Programming vs User-Space Programming                            │
│                                                                            │
│ • No libc / standard C library                                             │
│   → use kernel headers: <linux/...>                                      │
│                                                                            │
│ • No memory protection                                                     │
│   → invalid pointer may crash the kernel                                   │
│                                                                            │
│ • No floating-point operations                                             │
│   → prefer integer arithmetic                                              │
│                                                                            │
│ • Very small kernel stack (typically 4–8 KB)                               │
│   → use kmalloc() for large memory buffers                                 │
│                                                                            │
│ • SMP + interrupts                                                         │
│   → synchronization and race conditions matter                             │
│                                                                            │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│ 6. Types of Linux Device Drivers                                           │
│                                                                            │
│ Character Drivers ('c')                                                    │
│ → byte-stream devices                                                      │
│ → examples: /dev/ttyX, keyboards, serial ports                             │
│                                                                            │
│ Block Drivers ('b')                                                        │
│ → block-oriented storage access                                            │
│ → examples: /dev/sdaX, SSD, eMMC, NVMe                                     │
│                                                                            │
│ Network Drivers                                                            │
│ → packet-oriented communication                                            │
│ → no /dev node                                                             │
│ → examples: eth0, wlan0, hci0                                              │
│                                                                            │
├────────────────────────────────────────────────────────────────────────────┤
│                                                                            │
│ 7. Linux Device Numbering Model                                            │
│                                                                            │
│ major number                                                               │
│ → identifies the driver                                                    │
│                                                                            │
│ minor number                                                               │
│ → identifies the specific device instance                                  │
│                                                                            │
│ Complete Access Path:                                                      │
│                                                                            │
│ Application                                                                │
│      ↓                                                                     │
│ System Call Interface                                                      │
│      ↓                                                                     │
│ Virtual File System (VFS)                                                  │
│      ↓                                                                     │
│ /dev node (major, minor)                                                   │
│      ↓                                                                     │
│ Device Driver                                                              │
│      ↓                                                                     │
│ Hardware                                                                   │
│                                                                            │
└────────────────────────────────────────────────────────────────────────────┘

🚀 Continue Your Linux Kernel Journey

This is part of our free Advanced Linux Development Course on EmbeddedPathashala. All lectures are available for free — no login required.

🌐 Visit EmbeddedPathashala 📚 Full Course Index

Questions? Drop them in the comments below ↓  ·  Found an error? Let us know!

Leave a Reply

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