System Driver Types (Linux)
User Space Driver
When full access to a device's resources is not required, a User Space Driver (USD) can utilize system interfaces for limited access to the device's memory space and handling interrupts1.
If the driver needs to access privileged functions, such as ioperm()
system calls to access x86 I/O ports, root
-privileges are necessary.
User Space Drivers offer a restricted alternative to Kernel Space Drivers. Their key advantage lies in improved system stability: If a bug slips into the User Space Driver, only the corresponding process is at risk of crashing, not the whole system.
Kernel Space Driver
Kernel Space Drivers are embedded into the kernel space and provide full access to a device's memory and I/O ports. They can be statically linked into the kernel, requiring a full kernel compilation whenever the implementation of the driver changes. Errors in the driver itself will most likely negatively affect the stability of the whole system.
As an alternative, Loadable Kernel Modules (LKM) allow drivers to be dynamically inserted into a running kernel without recompilation2. System directives such as insmod
can be used for loading these modules.
LKMs help reduce kernel size and compile times, accelerating development cycles (load
test
unload
).
However, they might introduce a slight performance overhead due to the additional abstraction layer between the module and the kernel.
insmod
/ rmmod
The following excerpt from a bash script demonstrates the usage of insmod
/ rmmod
: The call to rmmod
removes the specific module if it was already loaded4, insmod
loads it accordingly.
#! /bin/bash
sudo rmmod ${MODULE} 2>/dev/null
# loads the module
sudo insmod ${MODULE}.ko || { echo "Error loading ${MODULE}.ko"; exit 1; }
init_module
/ cleanup_module
Additionally, there exist two functions in the Linux-Kernel library <linux/module.h>
that can be used as callbacks when the LKM has been loaded / unloaded for further bootstrapping module functionality, requesting system resources and freeing them, respectively:
-
init_module
: Called when the module is initialized (insmod
) -
cleanup_module
: Called when the module is unloaded (rmmod
)
The following listing5 shows the usage of init_module()
and cleanup_module()
. Take note that
device_major
specifies the major device number6 associated with the device implemented by the module. We set this to0
and expect the kernel to dynamically allocate a device identifier.register_chrdev
/unregister_chrdev
takes care of (un)registering the module with the kernel:register_chrdev
provides information to the kernel which file-operations are mapped to which functions in the specific module.unregister_chrdev
de-registers this information accordingly.
#include <linux/module.h>
#include <linux/fs.h>
...
#define MODULE_NAME "myModule"
static int device_major = 0;
struct file_operations module_fops = {
.owner = THIS_MODULE,
// ...
};
int init_module(void) {
printk(KERN_INFO "%s init start:\n", MODULE_NAME);
int result = register_chrdev(device_major, MODULE_NAME, &module_fops);
if (result < 0) {
printk(KERN_ERR " Failed to register %s: Error %d\n", MODULE_NAME, result);
return result;
}
if (device_major == 0) {
device_major = result;
}
...
return 0;
}
void cleanup_module(void) {
printk(KERN_INFO "%s cleanup start:\n", MODULE_NAME);
unregister_chrdev(device_major, MODULE_NAME);
...
}
Footnotes
-
https://www.kernel.org/doc/html/latest/driver-api/uio-howto.html, retrieved 01.05.2025 ↩
-
https://en.wikipedia.org/wiki/Loadable_kernel_module, retrieved 01.05.2025 ↩
-
The man-pages specifically point to
modprobe
which takes care of unloading interdependent modules ↩ -
while ignoring possible error messages like
rmmod: ERROR: Module is not currently loaded
↩ -
static int device_major
takes care of limiting the scope ofdevice_major
to the compilation unit ([📖KR88, p. 83]) ↩ -
'Device nodes and major/minor numbers': https://www.ibm.com/docs/en/linux-on-systems?topic=hdaa-device-nodes-numbers, retrieved 02.05.2025 ↩