What You Are Building
The simplest useful kernel driver is often a loadable kernel module, built as a
.ko file. The example here creates a tiny character device called
/dev/hello_kmod. When userspace reads from it, the driver returns a
short message.
Main Functions
A small character-device module has a few core pieces:
hello_init: runs when the module is loaded withinsmod. It registers the character device and creates/dev/hello_kmod.hello_exit: runs when the module is unloaded withrmmod. It destroys the device and unregisters the major number.hello_open: runs when a process opens the device file.hello_read: copies data from kernel memory to userspace withcopy_to_user.hello_release: runs when the process closes the device file.struct file_operations: connects userspace file actions to your driver callbacks.module_initandmodule_exit: tell the kernel which functions are the module entry and exit points.
Procedure
- Install the kernel headers/build files for your running kernel.
- Write a module source file, usually something like
hello_kmod.c. - Write a kernel-module
Makefileusingobj-m. - Build with the kernel build system, not with a plain
gcc hello_kmod.c. - Load the module with
sudo insmod hello_kmod.ko. - Check logs with
dmesgorjournalctl -k. - Test the device from userspace.
- Unload it with
sudo rmmod hello_kmod.
Sample Driver C File
The full file is available at examples/simple_kernel_driver/hello_kmod.c. This is the core shape:
// SPDX-License-Identifier: GPL-2.0
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#define DEVICE_NAME "hello_kmod"
#define CLASS_NAME "hello_class"
static int major_number;
static struct class *hello_class;
static struct device *hello_device;
static const char message[] = "hello from a tiny kernel driver\n";
static ssize_t hello_read(struct file *file, char __user *buffer,
size_t length, loff_t *offset)
{
size_t message_len = sizeof(message) - 1;
size_t bytes_to_copy;
if (*offset >= message_len)
return 0;
bytes_to_copy = min(length, message_len - (size_t)*offset);
if (copy_to_user(buffer, message + *offset, bytes_to_copy))
return -EFAULT;
*offset += bytes_to_copy;
return bytes_to_copy;
}
static const struct file_operations hello_fops = {
.owner = THIS_MODULE,
.open = hello_open,
.read = hello_read,
.release = hello_release,
};
module_init(hello_init);
module_exit(hello_exit);
The real file includes the complete open, release,
init, and exit implementations.
Sample Makefile
Yes, this is the normal/recommended way to build an out-of-tree Linux kernel module. You let the kernel build system compile the module because it knows the correct flags, generated headers, symbol versions, and module metadata for the running kernel.
obj-m += hello_kmod.o
KDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
The full file is available at examples/simple_kernel_driver/Makefile.
How To Use This Makefile
On Ubuntu/Debian, install the headers for the kernel you are currently running:
sudo apt update
sudo apt install build-essential linux-headers-$(uname -r)
Then build the module from the directory containing hello_kmod.c and Makefile:
cd examples/simple_kernel_driver
make
The important output is:
hello_kmod.ko
That .ko file is the loadable kernel module. The temporary files
such as .mod, .o, Module.symvers, and
modules.order are normal kernel-build artifacts.
Load And Test
Load the module:
sudo insmod hello_kmod.ko
Confirm it loaded:
lsmod | grep hello_kmod
dmesg | tail
Read from the device:
cat /dev/hello_kmod
You should see:
hello from a tiny kernel driver
Unload And Clean
Unload the module:
sudo rmmod hello_kmod
Remove build artifacts:
make clean
rmmod says the module is busy, close any shell or program that
still has /dev/hello_kmod open, then try again.