Skip to content

hello kernel module

Harry Broeders edited this page May 22, 2026 · 2 revisions

Kernel module in Linux

In Linux is een kernel module een stuk code dat kan worden geladen in de kernel om extra functionaliteit toe te voegen. Kernel modules worden vaak gebruikt om stuurprogramma's voor hardware te implementeren, maar ze kunnen ook andere taken uitvoeren.

Kernel modules worden geschreven in de programmeertaal C of Rust en moeten voldoen aan bepaalde richtlijnen en structuren om correct te functioneren binnen de kernel.

Een typisch voorbeeld van een eenvoudige kernel module is een "Hello Goodbye" module, die een bericht naar de kernel log schrijft wanneer de module wordt geladen en een ander bericht wanneer de module wordt verwijderd. Dit kan worden bereikt met behulp van de printk functie, die vergelijkbaar is met de printf functie in gebruikersruimte, maar geschikt is voor gebruik in de kernel. Output van printk kan worden bekeken met het dmesg commando. Bij het gebruik van printk kan een log level worden opgegeven om de belangrijkheid van het bericht aan te geven, zoals KERN_INFO voor informatieve berichten of KERN_ERR voor foutmeldingen. Er zijn macro's beschikbaar zoals pr_info() en pr_err() die automatisch het juiste log level toevoegen aan de berichten. Om een kernel module te laden, wordt het insmod commando gebruikt. Om een module te verwijderen, wordt het rmmod commando gebruikt. Voor het laden en verwijderen van kernel modules zijn root rechten vereist.

Kernel modules bieden een manier om de functionaliteit van de Linux-kernel uit te breiden zonder de noodzaak om de kernel zelf opnieuw te compileren. Dit maakt het gemakkelijker om nieuwe hardware-ondersteuning toe te voegen of bestaande functionaliteit te wijzigen zonder de stabiliteit van het systeem in gevaar te brengen.

Hieronder is de C-code van een eenvoudig voorbeeld van een "Hello Goodbye" kernel module gegeven (hello_kernel_module.c):

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>

static int __init hello_init(void) {
	pr_info("Hello, from your kernel module\n");
	return 0;
}

static void __exit hello_exit(void) {
	pr_info("Goodbye, from your kernel module\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Harry Broeders");
MODULE_DESCRIPTION("Hello kernel module");

Met behulp van de macro's module_init en module_exit worden de functies hello_init en hello_exit geregistreerd als respectievelijk de initialisatie- en opruimfunctie van de module. Deze macro's zijn gedefinieerd in het header bestand linux/module.h.

Deze functies zijn static gedefinieerd, wat betekent dat ze alleen binnen dit bronbestand toegankelijk zijn.

__init en __exit zijn speciale macro's die aangeven dat de functies alleen tijdens het laden en verwijderen van de module worden gebruikt. Deze macro's zijn gedefinieerd in het header bestand linux/init.h. De compiler kan hierdoor optimalisaties toepassen, zoals het vrijgeven van geheugen dat alleen tijdens de initialisatie nodig is nadat de initialisatie is voltooid.

Met behulp van de macro's MODULE_LICENSE, MODULE_AUTHOR en MODULE_DESCRIPTION wordt metadata over de module opgegeven, zoals de licentie, de auteur en een korte beschrijving van de module.

Deze code kan worden gecompileerd tot een kernel module met behulp van de volgende Makefile:

obj-m += hello_kernel_module.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Om de module te compileren, voer je het volgende commando uit in de directory waar de broncode en de makefile zich bevinden:

$ make
make -C /lib/modules/6.6.22-1-lts-CSC10-csc10/build M=/home/student/CSC10_Development/hello_kernel_module modules
make[1]: Entering directory '/usr/lib/modules/6.6.22-1-lts-CSC10-csc10/build'
make[1]: Leaving directory '/usr/lib/modules/6.6.22-1-lts-CSC10-csc10/build'

Dit zal onder andere een bestand genaamd hello_kernel_module.ko genereren, wat de gecompileerde kernel module is.

De matadata van de module kan worden bekeken met het commando:

$ modinfo hello_kernel_module.ko
filename:       /home/student/CSC10_Development/hello_kernel_module/hello_kernel_module.ko
description:    Hello kernel module
author:         Harry Broeders
license:        GPL
depends:        
name:           hello_kernel_module
vermagic:       6.6.22-1-lts-CSC10-csc10 SMP mod_unload ARMv7 p2v8 

Deze module kan vervolgens worden geladen met het commando:

$ sudo insmod hello_kernel_module.ko

Je kunt de laatste twee kernel messages bekijken met het commando:

$ dmesg -H | tail -2
[Nov22 14:56] hello_kernel_module: loading out-of-tree module taints kernel.
[  +0.000272] Hello, from your kernel module

De optie -H van het commando dmesg zorgt voor een menselijk (Human) leesbare format van tijdstempels. De uitvoer van het dmesg commando wordt met het pipe symbool (|) doorgegeven aan het tail commando. De optie -2 van het tail commando zorgt ervoor dat alleen de laatste 2 regels van de invoer worden getoond.

Als eerste meldt de kernel dat de kernel "tainted" (Nederlands: besmet) is omdat er een out-of-tree module is geladen (een module die geen deel uitmaakt van de officiële kernel broncode). Een "tainted" kernel betekent dat de kernel mogelijk onstabiel is geworden door het laden van niet-officiële code. De status van de kernel kan worden gecontroleerd met het commando cat/proc/sys/kernel/tainted.

$ cat /proc/sys/kernel/tainted
1024

Als de waarde van tainted niet nul is, betekent dit dat de kernel "tainted" is. De waarde 1024 betekent dat er een out-of-tree module is geladen (bit 10 is gezet), zie https://docs.kernel.org/6.6/admin-guide/tainted-kernels.html.

De tweede regel van de uitvoer van dmesg is het bericht dat door de functie hello_init() uit onze kernel module is gegenereerd bij het laden van de module door het aanroepen van pr_info().

Je kunt alle geladen modules bekijken met het commando lsmod:

$ lsmod
Module                  Size  Used by
hello_kernel_module    12288  0
... andere modules ...

Om de module te verwijderen, gebruik je het commando:

$ sudo rmmod hello_kernel_module

Je kunt de laatste kernel messages bekijken met het commando:

$ dmesg -H | tail -1
[Nov22 17:06] Goodbye, from your kernel module

Je ziet nu het bericht dat door de functie hello_exit() uit onze kernel module is gegenereerd bij het verwijderen van de module door het aanroepen van pr_info().

Een kernel module die ons netjes groet bij het laden en verwijderen is natuurlijk leuk, maar laat niet zien dat de module daadwerkelijk iets doet wat niet mogelijk is vanuit user space zonder root rechten. Om dit te demonstreren wordt in de onderstaande led_kernel_module.c een led aangezet als de module wordt geladen en uitgezet als de module wordt verwijderd. We gaan er hierbij vanuit dat de led aan te sturen is via bit 0 van het 32-bit geheugenadres 0xFF200010.

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/io.h>

static void __iomem *io_ptr;

static int __init led_init(void) {
	io_ptr = ioremap(0xFF200010, 4);
	if (io_ptr == NULL) {
		pr_err("ERROR: memory of device can not accessed\n");
		return -ENOMEM;
	}
	writel(readl(io_ptr) | (1<<0), io_ptr);
	return 0;
}

static void __exit led_exit(void) {
	writel(readl(io_ptr) & ~(1<<0), io_ptr);
	iounmap(io_ptr);
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Harry Broeders");
MODULE_DESCRIPTION("Led kernel module");

Met de functie ioremap() wordt een virtuele geheugenpointer verkregen die toegang geeft tot het 32-bit (4 bytes) register op het fysieke adres 0xFF200010. Deze pointer wordt opgeslagen in de variabele io_ptr van het type void __iomem *. De macro __iomem geeft aan dat deze pointer verwijst naar een geheugengebied dat I/O-poorten bevat.

Deze pointer wordt gebruikt met de functies readl() en writel() om respectievelijk de huidige waarde van het register te lezen en een nieuwe waarde naar het register te schrijven.

Bij het initialiseren van de module wordt bit 0 van het register op 1 gezet om de led aan te zetten. Bij het verwijderen van de module wordt bit 0 van het register op 0 gezet om de led uit te zetten.

Met behulp van de functie iounmap() wordt de virtuele geheugenmapping weer opgeheven bij het verwijderen van de module.

Clone this wiki locally