User Tools

Site Tools


linux-kernel-dev:capitole:capitol-02

This is an old revision of the document!


2. Module de kernel

Cel mai facil și convenabil mod de a face dezvoltare la nivelul nucleului este prin module de kernel. Modulele de kernel reprezintă fișiere obiect care pot aduce în mod dinamic (la rulare) funcționalități nucleului.

Subiecte abordate

  • Nevoia de module de kernel, ce este un modul de kernel
  • Aflarea de informații despre module de kernel
  • Încărcarea și descărcarea unui modul de kernel
  • Compilarea unui modul de kernel
  • Conținutul unui modul de kernel: funcții de inițializare și de ieșire
  • Conectarea modului de kernel la nucleu; extinderea funcționalităților
  • Module de kernel din fișiere cod sursă multiple

Resurse utile

Exerciții

Arhiva de suport pentru exerciții se găsește aici. Descărcați arhiva și apoi decomprimați-o folosind comanda

unzip cap-02-tasks.zip

În prealabil, trebuie să refaceți scripturile din codul sursă al nucleului. Pentru aceasta accesați directorul linux-kernel-dev/linux-3.13 și rulați comanda

make scripts

Afișarea de informații despre module

Orice sistem de operare (nu doar Linux) folosește module pentru extinderea în mod dinamic a funcționalităților sistemului de operare. De multe ori acestea sunt forma în care sunt implementatea driverele de dispozitive (device drivers). Înainte de opera modulele este util să putem afla informații despre module.

Sunt două operații frecvente de aflare de informații și de investigare a modulelor de kernel:

  1. Afișarea modulelor curente
  2. Afișarea de informații despre module

Pentru a afișa modulele curente în nucleul Linux folosim comanda

lsmod

Această comandă o putem folosi și pe mașina virtuală VirtualBox și pe mașina virtuală QEMU. O altă formă de afișare a acelorași informații este comanda

cat /proc/modules

Pentru a afișa informații despre un modul în parte se folosește comanda

sudo modinfo <nume-modul>

unde <nume-modul> este numele modulului. Se pot afișa informații și despre modulele încărcate și cele care nu au fost încărcate.

Modulele se găsesc de regulă în directorul /lib/modules/<kernel-version>. Comanda modinfo poate fi folosită doar dacă modulele se găsesc în acel director, motiv pentru care comanda va funcționa doar pe mașina virtuală VirtualBox.

Alte informații, dinamice, despre modulele existente în acel moment în kernel (fie compilate direct, built in, fie module încărcabile/descărcabile) se găsesc în /sys/module/.

Aflați informații despre un modul de kernel din mașina virtuală VirtualBox și despre un modul de kernel din mașina virtuală QEMU folosind intrările din /sys/module/.

Încărcarea și descărcarea unor module de sistem

Multe dintre modulele folosite de nucleu sunt module “de sistem”, adică sunt deja compilate și instalate. De obicei acestea rezită în directorul /lib/modules/<kernel-version>/.

Aceste module pot fi descărcate încărcate la nevoie. Întrucât aceste module se găsesc doar pe mașina virtuală VirtualBox, doar acolo putem să operăm cu acestea.

Pentru a descărca un modul de sistem de pe mașina virtuală VirtualBox, să spunem modulul lp, folosim una dintre comenzile:

sudo modprobe -r lp
sudo rmmod lp

Dacă se vor afișa din nou modulele din kernel, se va vedea că modulul lp lipsește.

Pentru a reinsera modulul de kernel lp avem, din nou, două opțiuni

sudo modprobe lp
sudo insmod /lib/modules/3.16.0-4-586/kernel/drivers/char/lp.ko

Comanda insmod primește calea completă către modulul de kernel, un fișier cu extensia .ko. Calea completă o putem afla cu ajutorul comenzii modinfo.

Comanda modprobe poate fi folosită doar pe module de sistem, cele localizate în directorul /lib/modules/<kernel-version/. Comenzile insmod și rmmod pot fi folosite pentru orice fel de module, inclusiv module “custom”, motiv pentru care le vom folosi preponderent de acum încolo.

Copiați modulul lp.ko în mașina virtuală QEMU, în fsimg/root/modules și apoi încercați să-l încărcați folosind comanda

insmod lp.ko

De ce nu puteți să încărcați modulul în kernel?

Încărcarea și descărcarea unui modul custom

În directorul cap-02-bin/ din arhiva de laborator găsiți modulul de kernel hello.ko. Vrem să încărcăm și să descărcăm acest modul în mașina virtuală QEMU.

Copiați modulul pe mașina virtuală QEMU și porniți mașina virtuală.

Folosiți insmod și rmmod pentru a încărca și descărca modulul din kernel. Observați mesajele afișate în momentul încărcării și descărcării modulului din kernel.

Compilarea unui modul de kernel

După cum am precizat în capitolul trecut, mașina virtuală QEMU este minalistă și folosită doar pentru a testa (rapid) funcționalitate. Compilarea modulelor de kernel pe care le vom folosi în cadrul mașinii virtuale o vom face pe mașina virtuală VirtualBox.

Pentru a compila un modul de kernel acesta trebuie linkat la versiunea de nucleu corespunzătoare nucleului în care va fi inserat. De aceea, întotdeauna vom referi în procesul de compilare directorul cu sursele nucleului, în cazul de față /home/training/linux-kernel-dev/linux-3.13/.

În subdirectorul hello/ din directorul cap-02-skel/ avem codul sursă (hello.c) și fișierele de compilare Makefile și Kbuild pentru compilarea modului. În mașina virtuală VirtualBox folosim, în directorul hello/, comanda

make

pentru a compila modulul de kernel. În urma compilării rezultă fișierul hello.ko pe care îl vom copia în mașina virtuală QEMU și apoi pornim mașina virtuală și încărcăm și descărcăm modulul de kernel.

Aceștia sunt pașii uzuali pentru dezvoltarea și testarea modulului de kernel. Editarea/implementarea și compilarea au loc pe mașina virtuală VirtualBox, în timp ce testarea sa (încărcare și descărcare) au loc pe mașina virtuală QEMU.

Afișare mesaj și la descărcare

Actualizați modulul hello.c pentru a afișa un mesaj și la descărcarea din kernel, la operația rmmod.

Folosiți funcția printk astfel încât mesajele să fie afișate în bufferul kernel-ului (adică să fie afișate cu dmesg) dar să nu fie afișate la consolă.

Folosiți nivelul corespunzător de logging la printk, inferior celui afișat în fișierul /proc/sys/kernel/printk. Nivelurile de logging se găsesc în codul sursă al nucleului, în fișierul include/linux/kern_levels.h.

Afișare PID

Actualizați modulul hello.c pentru a afișa PID-ul procesului curent atât la încărcarea modulului de kernel cât și la descărcarea acestuia.

Pointer-ul la procesul curent este dat de macro-ul current, găsit în include/linux/sched.h. Macro-ul este un pointer al structurii struct task_struct definită, de asemenea, în include/linux/sched.h.

Afișare comandă

Actualizați modulul hello.c pentru a afișa PID-ul și executabilul/comanda aferentă procesului curent, atât la încârcarea modulului în kernel cât și la descărcarea acestuia.

Comanda/executabilul aferent unui proces este dat de câmpul comm al structurii struct task_struct aferente.

Afișare informații despre procesul părinte

Actualizați modulul hello.c pentru a afișa PID-ul și executabilul/comanda aferentă procesului părinte al procesului curent, atât la încârcarea modulului în kernel cât și la descărcarea acestuia.

Urmăriți în cadrul structurii struct task_struct care este câmpul cu ajutorul căruia se determină procesul părinte (tot un pointer la o structură de tipul struct task_struct.

Exemplu de hook: firewall minimal

În directorul hook/ din arhiva de suport capitolului se găsește un exemplu de folosire a framework-ului netfilter din cadrul nucleului Linux. Este framework-ul folosit și de utilitarul iptables.

Parcurgeți codul sursă, observați ce se întâmplă și apoi obțineți modulul și testați-l în mașina virtuală QEMU.

Pentru testare folosiți ping din mașina virtuală VirtualBox către mașina virtuală QEMU

ping 172.20.0.2

Adresa 172.20.0.2 este adresa interfeței eth0 a mașinii virtuale QEMU.

Urmăriți numerele de secvență ale mesajelor ICMP în output-ul comenzii ping, adică partea cu icmp_seq.

Și apoi pe mașina virtuală QEMU încărcați modulul hook.ko. Observați acum care sunt numerele de secvență ale mesajelor ICMP. Observați că acum un pachet din două nu este afișat, pentru că este filtrat de modul.

Descărcați modulul hook.ko și observați că acum numerele de secvență revin la numere consecutive, nemaifiind filtrate de modul.

Modul din surse multiple

Uneori dorim să separăm funcționalitatea unui modul în fișiere multiple pentru a nu încărca tot codul sursă într-un singur fișier. În acea situația avem nevoie de o actualizare a modului în care sunt constituite fișierele Kbuild și Makefile.

În directorul multi/ din directorul cap-02-skel/ din arhiva capitolului există un exemplu (academic) de modul de kernel din surse multiple. Urmăriți conținutul acestora și compilați fișierele folosind comanda

make

Urmăriți fișierele intermediare și fișierul modul final.

Copiați fișierul modul final în mașina virtuală QEMU, porniți mașina virtuală QEMU și încărcați și descărcați modululul din kernel.

Modul nou din surse multiple

Creați un modul care să aibă două fișiere cod sursă. Într-un fișier cod sursă sunt implementate funcțiile de inițializare și ieșire ale modulului. În celălalt fișier implementați o funcție care face dump în hexacimal la cel mult 4096 de octeți de la o adresă dată. Adresa dată trebuie să fie adresă de kernel space (>= 0xc0000000). Funcția este apelată din primul modul.

Compilați modulul, copiați-l în mașina virtuală QEMU și testați-l.

Actualizare modul de hook

Actualizați modulul de hook prin adăugarea unei noi structuri de operații și a unei funcții care să respingă pachetele de tipul echo reply care pleacă de la stația locală, o dată la 3 pachete.

Pentru pachetele care pleacă folosiți ca hooknum valoarea NF_INET_LOCAL_OUT.

[BONUS] Afișarea unui șir primit ca parametru pentru modul

Actualizați scriptul hello.c astfel încât să primească un parametru message care să fie inițializat la un șir. Acel șir este afișat folosind printk la inserarea modulului.

Indicații sunt în directorul cap-02-doc/ din arhiva capitolului.

linux-kernel-dev/capitole/capitol-02.1441705825.txt.gz · Last modified: 2015/09/08 12:50 by razvan