Drivers and the Kernel
The Kernel
Pithy
The kernel a layer between the user space and hardware.
It has an API (syscalls) that provide useful facilities for interacting with the hardware.
The kernel API interface provides five basic features:
- Management and abstraction of hardware devices
- Processes and threads (and ways to communicate among them)
- Management of memory (virtual memory and memory-space protection)
- I/O facilities (filesystems, network interfaces, serial interfaces, etc.)
- Housekeeping functions (startup, shutdown, timers, multitasking, etc.)
Note
Kernel is mostly C, with assembly sprinkled in where C can’t reach low-level CPU features (like atomic read-modify-write instructions).
Kernel versioning
Some distributions choose to emphasize stability and so run the older, more tested kernels.
Other distributions try to offer the most recent device support and features but might be a bit less stable as a result.
Select among these options in a manner that accommodates your users’ needs. No single solution is appropriate for every context.
Linux kernels use semantic versioning: major, minor and patch level
uname # = unix name, prints os name, kernel version, hw, etc.)
# Ubuntu machine
uname -r # see what kerenal the system is running
6.8.0-117-generic
6 # major
8 # mintor
0 # patch
-177-generic # ubuntu specific build number and flavor
# Debian machine
6.12.90+deb13.1-amd64Tip
If you need a more recent kernel, install a distribution that’s designed around that kernel rather than trying to shoehorn the new kernel into an existing system.
As an example Debian 13 ships with 6.12.X and will stay on that through it’s lifecycle, they won’t change minors or majors for all of Debian 13 Source.
Kernel LTS versions are released around every year: https://www.kernel.org/category/releases.html Your distribution handles kernel updates for you, you just get whatever they ship.
| Distribution | Release | Kernel Series | Type |
|---|---|---|---|
| Arch Linux | Rolling | Latest | Mainline |
| Debian 13 (Trixie) | Testing | 6.12 | Longterm |
| Ubuntu 24.10 | Regular | 6.11 | Stable |
| Fedora 41 | Regular | 6.11 | Stable |
| Ubuntu 24.04 LTS | Stable | 6.8 | Longterm |
| Debian 12 (Bookworm) | Stable | 6.1 | Longterm |
| RHEL 9 | Stable | 5.14 | Longterm |
Kernel maintainers actively back port security and bug fixes from mainline to each long term series. Which is why Ubuntu is 177, all of the patches they’ve had provided by upstream kernel maintainers. Ubuntu just packages, the kernel maintainers do the hard work.
How to get the latest patch of your kernel
apt list --upgradable
sudo apt update
sudo apt upgradeBranches
Users: don’t care. Your distribution handles it.
- Prepatch/RC - test new features
kernel developers - Mainline - new features released every 9-10 weeks
kernel developers - Stable - bug fixes from mainline, weekly releases
distro maintainers - Longterm - backported security fixes for 2+ years
distro maintainers & enterprises
Drivers
Pithy
A driver translates between a specific hardware device and the kernel’s standard interface.
A device driver is an abstraction layer that manages the system’s interaction with a particular type of hardware so that the rest of the kernel doesn’t need to know its specifics. The driver translates between the hardware commands understood by the device and a stylized programming interface defined (and used) by the kernel. The driver layer helps keep the majority of the kernel device-independent.
Drivers for other operating systems (e.g., Windows) do not work on UNIX and Linux.
Device files
In most cases, device drivers are part of the kernel; they are not user processes. However, a driver can be accessed both from within the kernel and from user space, usually through “device files” that live in the /dev directory.
The files in /dev each have a major and minor device number associated with them. The kernel uses these numbers to map device-file references to the corresponding driver.
Important
When a program performs an operation on a device file:
- the kernel intercepts the reference
- looks up the appropriate function name in a table
- transfers control to the appropriate part of the driver
Pseudo Devices
It is sometimes convenient to implement an abstraction as a device driver even when it controls no actual device.
Such phantom devices are known as pseudo-devices.
Example, a user who logs in over the network is assigned a pseudo-TTY (PTY) that looks, feels, and smells like a serial port from the perspective of high-level software.
This trick allows programs written in the days when everyone used a physical terminal to continue to function in the world of windows and networks. /dev/zero, /dev/null and /dev/urandom are some other examples of pseudo-devices.
Driver Example
Samsung Bar 3.1 plugged in:
- Kernel detects device, loads USB mass storage driver
- Driver creates
/dev/sdb, auto-mounts to/media/samsung - User:
cp test.txt /media/samsung/test.txt - The following pseudocode runs:
// User opens file on mounted filesystem
open("/media/samsung/test.txt", O_WRONLY | O_CREAT)
// Kernel creates file descriptor pointing to inode on /dev/sdb
// fd = 3
// write() syscall for the mounted filesystem
write(fd, "hello world", 11)
// We're calling write on /dev/sdb
// Kernel intercepts, translates to block device write
block_write(/dev/sdb, sector_123, data)
// Kernel looks up driver for major:minor 8:0
driver_function = driver_table[8][0]
// Calls driver
// Samsung bar is a generic USB device so it uses a generic USB driver
usb_mass_storage_write(sector_123, data)
// Driver sends USB WRITE command to Samsung Bar
// Device writes to flash, returns success
Device file management
When a new device is detected, both systems automatically create the device’s corresponding device files.
When a device goes away (e.g., a USB thumb drive is unplugged), its device files are removed.
/dev: interface to talk to the device/sys: metadata about the device
Example:
/dev/sdb → write data to it
/sys/block/sdb/size → query how many sectors it has
/sys/block/sdb/queue/hw_sector_size → query sector size
/sys/devices/pci0000:00/... → IRQ, power state, driver infoudev
udev is a daemon that:
- listens for when devices appear in
/sys - reads its metadata
- creates the appropriate
/dev/file with the right permissions - can run scripts/actions (mount, notify, etc.)
Example: plug in Samsung Bar -> udev creates /dev/sdb and auto-mounts it.
Without udev, you’d manually create /dev files for every device.
udevadm
udevadm is a tool that queries device info. It can be used to control the udevd daemon and help admins build and test rules for udev.
udevadm output
The results are layered kernel abstractions.
udevadm info -a -p /block/sdb/sdb1 Top layers = arbitrary kernel labels
Bottom layers = actual physical/electrical path from your USB port to the PCI bus
Rules and persistent names
udevd relies on a set of rules to guide its management of devices.
- default rules
/lib/udev/rules.d/*.rules - local rules
/etc/udev/rules.d/*.rules
You never need to edit or delete the default rules; you can ignore or override a default rules file by creating a new file with the same name in the custom rules directory.
Rules are in the form of:
match_clause, [match_clause, ...] assign_clause [assign_clause, ...]Match clauses
The match clauses define the situations in which the rule is to be applied.
The assignment clauses tell udevd what to do when a device is consistent with all the rule’s match clauses.
Most match keys refer to device properties (which udevd obtains from the /sys filesystem), but some refer to other context-dependent attributes, such as the operation being handled (e.g., device addition or removal).
| Match key | Function |
|---|---|
| ACTION | Matches the event type, e.g., add or remove |
| ATTR{filename} | Matches a device’s sysfs values |
| DEVPATH | Matches a specific device path |
| DRIVER | Matches the driver used by a device |
| ENV{key} | Matches the value of an environment variable |
| KERNEL | Matches the kernel’s name for the device |
| PROGRAM | Runs an external command; matches if the return code is 0 |
| RESULT | Matches the output of the last call through PROGRAM |
| SUBSYSTEM | Matches a specific subsystem |
| TEST{omask} | Tests whether a file exists; the omask is optional |
Assignment clauses
The most important assignment key is NAME, which indicates how udevd should name a new device.
Creating a rule
Insert my USB
lsusb Bus 001 Device 002: ID 413c:2113 Dell Computer Corp. KB216 Wired Keyboard Bus 001 Device 004: ID 6557:0021 Emtec USB DISK 2.0 # my USB # We can also watch the kernel logs to figure this out journalctl --dmsg -f Jun 07 13:17:49 main kernel: usb 1-5: Product: USB DISK 2.0 Jun 07 13:17:50 main kernel: sd 8:0:0:0: [sdb] 60628992 512-byte logical blocks: (31.0 GB/28.9 GiB) # The drive was recognized as sdb # So that means udevd will store the drive metadata in `/sys` ls /sys/block nvme0n1 sda sdbFind unique fields to match
# Use udevadm to investigate and find the matches we want to use udevadm info -a -p /block/sdb/sdb1 # Filter the results and use these to match this device ATTRS{model}=="USB DISK 2.0" ATTRS{serial}=="070823B935AD4425"Write a rule for this
sudo vim /etc/udev/rules.d/10-local.rules # Simple match for the serial number and write to a file when the drive is added (inserted) ACTION=="add", ATTRS{serial}=="070823B935AD4425", KERNEL=="sd[a-z]*", RUN+="/bin/sh -c 'echo matched >> /tmp/ate-flash-test.log'"Testing
# Plug in the USB cat /tmp/ate-flash-test.log matched # Success, the rule triggered