📋 Table of Contents
- What is the Linux Kernel?
- Linux Architecture: User Space vs Kernel Space
- System Call Interface — The Bridge
- The 5 Major Kernel Subsystems
- Kernel Programming ≠ Regular C Programming
- Classification of Device Drivers
- Major and Minor Device Numbers
- How an Application Accesses a Device
- What’s Coming Next
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.
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).
│ USER SPACE (Ring 3) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ bash │ │ vim │ │ gcc │ │ browser │ │ your │ │
│ │ shell │ │ editor │ │ compiler │ │ │ │ app │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└──────────────────────────┬──────────────────────────────────────────────┘
║ 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 │
└──────────────────────────────────────────────────────────────────────────┘
- 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)
- 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
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
(schedules which process runs on which core)
(physical page allocation, virtual memory maps)
(read/write in blocks)
(char/block device nodes)
(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).
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 = 2on 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.
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
You can trace all system calls made by any program using strace:
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.
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:
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:
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.
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)
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.
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:
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 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.
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.
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char *buf = malloc(128); printf(“Hello %s\n”, buf); free(buf); }
#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; }
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.
int *p = NULL; *p = 42; // → SIGSEGV // only this process dies // system keeps running
int *p = NULL; *p = 42; // → Oops: NULL pointer dereference // BUG: unable to handle kernel NULL pointer // entire system crashes or reboots
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).
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.
float percentage; percentage = (float)used / (float)total * 100.0; // NEVER do this in kernel code! // Corrupts FPU state of user processes
unsigned long percentage; percentage = (used * 100) / total; // Scale up by 100 first, then divide // Pure integer math — safe in kernel
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.
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 }
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; }
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)
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.
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)
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)
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)
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.
$ 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.
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 … |
┌────────────────────────────────────────────────────────────────────────────┐
│ 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:
┌────────────────────────────────────────────────────────────────────────────┐
│ 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 │
│ │
└────────────────────────────────────────────────────────────────────────────┘
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:
Understanding version numbers (6.x.y), LTS vs mainline vs stable kernels, and how to choose the right kernel for embedded projects.
Tour of the kernel directory structure: arch/, drivers/, fs/, include/, kernel/, mm/, net/. Where to find what.
Using make menuconfig, the .config file, and the Kconfig system. Enabling/disabling drivers. Building a minimal embedded kernel.
Building from source: make, vmlinuz, initrd, kernel modules (.ko files), cross-compilation for ARM.
Step-by-step: BIOS/UEFI → GRUB bootloader → kernel decompression → init process → systemd → user login.
Root filesystem structure, building a minimal RootFS with BusyBox, initramfs, and writing your first kernel module.
📝 Quick Revision Summary
┌────────────────────────────────────────────────────────────────────────────┐
│ 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 │
│ │
└────────────────────────────────────────────────────────────────────────────┘
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
