logo

Programowanie modułu jądra systemu Linux: program Hello World

Moduły jądra to fragmenty kodu, które można ładować i wyładowywać do jądra na żądanie. Rozszerzają funkcjonalność jądra bez konieczności ponownego uruchamiania systemu. Niestandardowe kody można dodawać do jąder Linuksa na dwa sposoby.
  • Podstawowym sposobem jest dodanie kodu do drzewa źródeł jądra i rekompilacja jądra.
  • Bardziej efektywnym sposobem jest dodanie kodu do jądra podczas jego działania. Proces ten nazywa się ładowaniem modułu, gdzie moduł odnosi się do kodu, który chcemy dodać do jądra.
Ponieważ ładujemy te kody w czasie wykonywania i nie są one częścią oficjalnego jądra Linuksa, nazywane są one ładowalnymi modułami jądra (LKM), które różnią się od jądra podstawowego. Jądro podstawowe znajduje się w katalogu /boot i jest zawsze ładowane podczas uruchamiania naszej maszyny, podczas gdy LKM są ładowane po załadowaniu jądra podstawowego. Niemniej jednak te LKM są w dużej mierze częścią naszego jądra i komunikują się z jądrem podstawowym, aby zakończyć swoje funkcje. LKM mogą wykonywać różnorodne zadania, ale zasadniczo dzielą się na trzy główne kategorie
  • sterownik urządzenia
  • sterownik systemu plików i
  • Wywołania systemowe.
Jakie więc zalety oferują LKM? Jedną z ich głównych zalet jest to, że nie musimy ciągle odbudowywać jądra za każdym razem, gdy dodajemy nowe urządzenie lub aktualizujemy stare urządzenie. Oszczędza to czas, a także pomaga w utrzymaniu wolnego od błędów jądra podstawowego. Przydatną praktyczną zasadą jest to, że nie powinniśmy zmieniać naszego podstawowego jądra, gdy mamy działające jądro podstawowe. Pomaga także w diagnozowaniu problemów systemowych. Załóżmy na przykład, że dodaliśmy moduł do podstawowego jądra (tzn. zmodyfikowaliśmy nasze podstawowe jądro poprzez jego rekompilację), a moduł zawiera błąd. Spowoduje to błąd podczas uruchamiania systemu i nigdy nie dowiemy się, która część jądra powoduje problemy. Natomiast jeśli załadujemy moduł w czasie wykonywania i spowoduje to problemy, natychmiast rozpoznamy problem i będziemy mogli wyładować moduł, dopóki go nie naprawimy. LKM są bardzo elastyczne w tym sensie, że można je załadować i rozładować za pomocą jednego wiersza polecenia. Pomaga to w oszczędzaniu pamięci, ponieważ ładujemy LKM tylko wtedy, gdy ich potrzebujemy. Co więcej, nie są wolniejsze od jądra podstawowego, ponieważ wywołanie jednego z nich polega po prostu na załadowaniu kodu z innej części pamięci. **Ostrzeżenie: LKM nie są programami przestrzeni użytkownika. Są częścią jądra. Mają swobodę działania systemu i mogą go łatwo zawiesić. So now that we have established the use loadable kernel modules we are going to write a hello world kernel module. That will print a message when we load the module and an exit message when we unload the module. Code: CPP
/**  * @file hello.c  * @author Akshat Sinha  * @date 10 Sept 2016  * @version 0.1  * @brief An introductory 'Hello World!' loadable kernel  * module (LKM) that can display a message in the /var/log/kern.log  * file when the module is loaded and removed. The module can accept  * an argument when it is loaded -- the name which appears in the  * kernel log files. */ #include  /* Needed by all modules */ #include  /* Needed for KERN_INFO */ #include  /* Needed for the macros */ ///< The license type -- this affects runtime behavior MODULE_LICENSE('GPL'); ///< The author -- visible when you use modinfo MODULE_AUTHOR('Akshat Sinha'); ///< The description -- see modinfo MODULE_DESCRIPTION('A simple Hello world LKM!'); ///< The version of the module MODULE_VERSION('0.1'); static int __init hello_start(void) {  printk(KERN_INFO 'Loading hello module...n');  printk(KERN_INFO 'Hello worldn');  return 0; } static void __exit hello_end(void) {  printk(KERN_INFO 'Goodbye Mr.n'); } module_init(hello_start); module_exit(hello_end); 
Wyjaśnienie powyższego kodu: Moduły jądra muszą mieć co najmniej dwie funkcje: funkcję „startu” (inicjalizacji) zwaną init_module(), która jest wywoływana, gdy moduł jest wstawiany do jądra, oraz funkcję „końca” (czyszczenia) zwaną cleanup_module(), która jest wywoływana tuż przed jego rmmodacją. Właściwie wszystko się zmieniło, począwszy od jądra 2.3.13. Możesz teraz używać dowolnej nazwy dla funkcji początkowej i końcowej modułu. W rzeczywistości nowa metoda jest metodą preferowaną. Jednak wiele osób nadal używa funkcji init_module() i cleanup_module() do funkcji początkowych i końcowych. W tym kodzie użyliśmy hello_start() jako funkcji inicjującej i hello_end() jako funkcji czyszczącej. Kolejną rzeczą, którą mogłeś zauważyć, jest to, że zamiast funkcji printf() użyliśmy printk(). Dzieje się tak dlatego, że moduł nie wydrukuje niczego na konsoli, ale zapisze wiadomość w pliku /var/log/kern.log. Dlatego służy do debugowania modułów jądra. Co więcej, w nagłówku zdefiniowanych jest osiem możliwych ciągów poziomu logowania, które są wymagane podczas korzystania z funkcji printk(). Podajemy je w kolejności malejącej ważności:
  • KERN_EMERG: Używany do komunikatów alarmowych, zwykle poprzedzających awarię.
  • KERN_ALERT: Sytuacja wymagająca natychmiastowego działania.
  • KERN_CRIT: Krytyczne warunki często związane z poważnymi awariami sprzętu lub oprogramowania.
  • KERN_ERR: Używany do zgłaszania warunków błędów; sterowniki urządzeń często używają KERN_ERR do zgłaszania problemów sprzętowych.
  • KERN_WARNING: Ostrzeżenia o problematycznych sytuacjach, które same w sobie nie powodują poważnych problemów z systemem.
  • KERN_NOTICE: Sytuacje, które są normalne, ale nadal godne uwagi. Na tym poziomie zgłaszanych jest szereg warunków związanych z bezpieczeństwem.
  • KERN_INFO: Wiadomości informacyjne. Wiele sterowników drukuje na tym poziomie informacje o sprzęcie, który znajdą podczas uruchamiania.
  • KERN_DEBUG: Używany do debugowania komunikatów.
  • Do wydrukowania wiadomości użyliśmy KERN_INFO. Przygotowanie systemu do uruchomienia kodu: The system must be prepared to build kernel code and to do this you must have the Linux headers installed on your device. On a typical Linux desktop machine you can use your package manager to locate the correct package to install. For example under 64-bit Debian you can use:
    akshat@gfg:~$ sudo apt-get install build-essential linux-headers-$(uname -r) 
    Makefile do kompilacji kodu źródłowego:
    obj-m = hello.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 
    **Uwaga: nie zapomnij o spacjach w Makefile Kompilacja i ładowanie modułu: Run the make command to compile the source code. Then use insmod to load the module.
    akshat@gfg:~$ make make -C /lib/modules/4.2.0-42-generic/build/ M=/home/akshat/Documents/hello-module modules make[1]: Entering directory `/usr/src/linux-headers-4.2.0-42-generic' CC [M] /home/akshat/Documents/hello-module/hello.o Building modules stage 2. MODPOST 1 modules CC /home/akshat/Documents/hello-module/hello.mod.o LD [M] /home/akshat/Documents/hello-module/hello.ko make[1]: Leaving directory `/usr/src/linux-headers-4.2.0-42-generic' 
    Now we will use insmod to load the hello.ko object.
    akshat@gfg:~$ sudo insmod hello.ko 
    Testowanie modułu: You can get information about the module using the modinfo command which will identify the description author and any module parameters that are defined:
    akshat@gfg:~$ modinfo hello.ko filename: /home/akshat/Documents/hello-module/hello.ko version: 0.1 description: A simple Hello world LKM author: Akshat Sinha license: GPL srcversion: 2F2B1B95DA1F08AC18B09BC depends: vermagic: 4.2.0-42-generic SMP mod_unload modversions 
    To see the message we need to read the kern.log in /var/log directory.
    akshat@gfg:~$ tail /var/log/kern.log ... ... Sep 10 17:43:39 akshat-gfg kernel: [26380.327886] Hello world To unload the module we run rmmod: akshat@gfg:~$ sudo rmmod hello Now run the tail command to get the exit message. akshat@gfg:~$ tail /var/log/kern.log ... Sep 10 17:43:39 akshat-gfg kernel: [26380.327886] Hello world Sep 10 17:45:42 akshat-gfg kernel: [26503.773982] Goodbye Mr.