Rašome kernelio modulį
Linux kernelio modulius galima rašyti dviem būdais: in-tree ir out-of-tree, o kompiliuoti dar dviem - kaip build-in (įkimpiluojamas į patį kernelį) ir kaip loadable-module (sukompiliuojamas kaip atskiras modulis, kurį galima užkrauti prireikus ar panorėjus).
Pats rašymas nelabai ir skiriasi, skiriasi tik kur laikomi failai. Paprastai rekomenduojama modulį rašyti in-tree ir prašyti, kad tavo modulį priimtų Linusas. Tada atseit kažkas pasirūpins, kad tavo modulis kompiliuotųsi keičiantis kernelio versijoms. Tačiau būkime realistai - niekam tavo modulis neįdomus, niekas jo nepriims ir niekas neprižiūrės.
Jeigu jau apėmė tokios niūrios nuotaikos arba dar tik mokaisi rašyti kernelio modulius, geriau rinktis out-of-tree metodą - bus lengviau susirasti savus failus ir sekti pakeitimus.
Rašome modulį
Pradėkime nuo paprasto “Sveikas pasauli!” moduliuko. Susikuriame naują failą:
1 |
|
Kas čia parašyta? Tuojau paaiškinsiu:
Sukuriame dvi statines funkcijas - inicializavimo ir apsivalymo. Kernelis suranda šias funkcijas pagal module_init() ir module_exit() makrosus.__init ir __exit makrosai skirti atminties valdymui. Įkompiliuotuose moduliuose __init makrosu pažymėta funkcija po iškvietimo bus atlaisvinta, o __exit funkcija bus ignoruojama. Dinamiškai užkraunamuose moduliuose (su modprobe/rmmod) šie makrosai nieko nereiškia. Mes rašysime dinamiškai užkraunamą modulį.
Makrosas printk yra skirtas loginimui į syslogą.
Kompiliuojame modulį
out-of-tree
Kompiluojant out-of-tree metodu mums net nereikia kernelio kodo, užtenka linux-headers, esančių:
/lib/modules/$(uname -r)/build
Paruošiame Makefile:
1 | obj-m += labukas.o |
Kompiliuojame:
1 | make |
Peržiūrime modulio informaciją:
1 | modinfo labukas.ko |
Prijungiame modulį prie veikiančio kernelio ir pažiūrime ar suveikė:
1 | sudo insmod ./labukas.ko |
Išjungiame modulį ir pažiūrime ar suveikė:
1 | sudo rmmod labukas |
Viskas, šiandienos darbas baigtas.
http://www.tldp.org/LDP/lkmpg/2.6/html/
in-tree
Kompiliuojant modulį in-tree iš pradžių reikės to tree - tai yra kernelio kodo. Jį parsisiunčiame iš https://www.kernel.org arba iš distribucijos repozitorijos, arba (jei esame visiškai pašėlę) iš github’o.
Kai jau gavome kodą galima į jį pridergti. Tarkime, kad mūsų modulis “labukas” yra draiveris. Todėl einame į aplanką drivers ir jame susikuriame naują aplanką - labukas. Į jį įmetame aukščiau nagrinėtą labukas.c ir sukuriame dar du papildomus failus:
1 | obj-$(CONFIG_LABAS_PASAULI_PVZ) += labukas.o |
1 | config LABAS_PASAULI_PVZ |
Makefile įsirašo sąlyga kada kompiliuoti mūsų modulį - tik kai pasirinktas CONFIG_LABAS_PASAULI_PVZ. KConfig faile - informacija apie modulį. Ši informacija bus matoma konfigūruojant kernelį pvz. per menuconfig. tristate reiškia, kad mūsų kodas gali būti ir kaip modulis ir įkompiliuotas. Jeigu norime leisti tik įkompiliuoti, turėtumėme vietoj tristate įrašyti bool.
Palauk, palauk, neskubėk! Dar ne viskas. Šiuos du papildomus failus irgi reikia užregistruoti.
Atsidarome drivers/Kconfig ir pridedame eilutę source "drivers/labukas/Kconfig". Tada atsidarome drivers/Makefile ir pridedame obj-y += labukas/.
Na dabar jau galime bandyti kompiliuoti: sugrįžtame į kernelio kodo šakninį aplanką ir paleidžiame make menuconfig, įeiname į Device Drivers ir štai - matosi mūsų modulis:

Klaviatūroje paspaudžiame klaustuką ir gauname išsamesnę informaciją ką šis konfigūracijos punktas reiškia:

Išeiname iš konfigūracijos. Išeinant paklaus ar norime išsaugoti pakeitimus - mes norime. Paleidžiame make ir kada nors kernelis, o kartu ir mūsų modulis, bus sukompiliuoti. Kaip jį pasileisti, tikiu, kuo puikiausiai visi žinote.
Kodą galima rasti githube