Table of Contents

4. Comunicarea între spațiul utilizator și spațiul kernel

Subiecte abordate

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-04-tasks.zip

Comandare CD-ROM folosind ioctl

Apelurile de tip ioctl() sunt folosite ca o extensie a apelurilor de sistem pentru a controla funcționalități ale unui dispozitiv sau a afla informații.

De exemplu, în directorul user-ioctl/ din arhiva de suport a capitolului găsiți două fișiere cod sursă care sunt folosite, respectiv pentru a deschide unitatea de CD-ROM a sistemului (eject) și pentru aflarea adresei hardware (MAC) a interfeței de rețea eth0.

Cele două fișiere sunt compilate folosind comanda make.

Pentru testarea fișierului de aflare a adresei hardware puteți folosi mașina virtuală VirtualBox:

./hwaddr-ioctl

Puteți testare fișierul de deschidere a unității de CD-ROM este nevoie de un sistem Linux cu unitate de CD-ROM pe care să rulați apoi comanda

./eject-cdrom-ioctl

Dacă folosiți fișierul de test pentru deschidere unității de CD-ROM pe un sistem care nu are unitate de CD-ROM (așa cum este cazul mașinii virtuale VirtualBox) veți primi un mesaj precum

(eject-cdrom-ioctl.c, 30): ioctl: Input/output error

Notificare folosind ioctl

Apelurile de tipul ioctl() pot avea mai multe funcționalități. Este recomandat să fie folosite doar la nevoie pentru a nu complica API-ul expus de sistemul de operare.

În directorul notify/ din arhiva de suport a capitolului se găsește o implementare a unui modul de kernel care asigură o sincronizare între două procese prin intermediul nucleului. Este un exercițiu didactic.

În subdirectorul kernel/ urmăriți codul sursă al modulului de kernel și apoi compilați-l.

În subdirectorul user/ compilați codul sursă al fișierului de test de user space și apoi compilați-l.

Copiați modulul de kernel obiect (notify.ko) și fișierul executabil de test (sync) pe mașina virtuală QEMU și apoi testați-le.

Pentru testare încărcați modulul de kernel. Apoi creați dispozitivul aferent

mknod /dev/notify c 42 0

Apoi folosiți pe câte o consolă operații de tipul wait și notify cu ajutorul fișierului de test, adică una dintre comenzile de mai jos:

./sync w
./sync n

Comenzile de tip wait blochează procesul curent, iar comenzile de tip notify îl eliberează. Pentru a putea rula mai multe console pentru a da comenzi folosiți, în mașina virtuală QEMU, combinațiile de tasta Alt+F1, Alt+F2 etc.

O singură comandă de tip notify eliberează un singur proces blocat.

Driver de upper-case

În general pentru partea de comunicarea inter-proces (IPC: Inter Process Communication) există mai multe facilități posibile expuse de kernel. Suntem mai degrabă preocupați de modul în care nucleul și un proces comunică.

Unul dintre modurile frecvente de comunicare este prin intermediul unui dispozitiv virtual, adică un dispozitiv care are o intrare de forma /dev/<name> dar care nu are în spate un dispozitiv fizic/hardware. Este cazul /dev/kvm sau /dev/fuse folosite de KVM (Kernel Virtual Machine) sau FUSE (Filesystem in User Space).

Pornind de la codul de la exercițiile din capitolul 3 realizați o implementare didactică de dispozitiv care realizaeză upper case la textul primit. Adică la operații de tip write() din user space, modulul citește informații pe care le stochează în bufferul său, iar la operații de tip read() din user space întoarce partea cu upper case a acelor informații care erau caractere mici (cuprinse între a și z).

Nu este nevoie de o implementare complicată de buffer precum un buffer circular. Alocați o dimensiune pentru bufferul de kernel și, la fiecare scriere se scrie în continuare până se umple bufferul. După aceea nu se poate decât citi. Se citește de la început până când se golește buffer-ul; chiar dacă s-a citit de la început doar o parte și ar fi în teorie spațiu, nu se poate scrie până când bufferul nu s-a golit complet.

Pentru testare să creați dispozitivul folosind comanda

mknod /dev/case c 42 0

Am presupus că numărul major al dispozitivului este 42 iar cel minor 0.

Pentru a testa scrierea în dispozitiv, folosiți o comandă de tipul

echo "abcdefgh" > /dev/case

iar pentru a testa citirea din dispozitiv folosiți o comandă de tipul

cat /dev/case

Comunicarea folosind maparea memoriei

O formă rapidă de comunicare, care evită apeluri de sistem este maparea memoriei. Aceasta permite un transfer rapid de informație între user space și kernel space dar are nevoie de o formă de sincronizare între cele două entități astfel încât o entitate să citească după ce a scris cealaltă.

În directorul mmap/ din arhiva de suport a capitolului găsiți o implementare de modul de kernel și utilitar de user space care pot comunica folosind maparea memoriei. După ce un apelul mmap() se întoarce în user space, zona de memorie aferentă va fi populată cu caractere a.

Va trebui să compilați atât modulul de kernel cât și utilitarul de test și apoi să le copiați pe mașina virtuală QEMU.

După ce ați pornit mașina virtuală QEMU, va trebui să încărcați modulul de kernel și apoi să folosiți fișierul executabil de test pentru testare. Executabilul de test creează și șterge automat fișierul de tip dispozitiv (/dev/mymmap) așa că nu mai este nevoie de rularea comenzii mknod.

Executabilul de test parcurge fiecare pagină și verifică dacă primii patru octeți ai fiecărei pagini conțin caracaterul a caz în care va afișa mesajul matched.

Driver de upper-case cu maparea memoriei

Actualizați componentele de user space și kernel space de mai sus astfel încât modulul să facă upper case la textul pe care procesul de user space îl scrie în zona de memorie mapată. Pentru ca nucleul să știe când să facă asta, folosiți un apel ioctl() care îi va comanda nucleului să transforme caracterele lower case din zona mapată în caractere upper case.

O altă formă de comunicare între kernel space și user space, care nu necesită operații speciale în user space (precum crearea de dispozitive) este folosind sockeți netlink așa cum este folosit în partea de configurare a rutării.

În directorul netlink/ din arhiva de suport a capitolului găsiți o implementare de modul de kernel și utilitar de user space care comunică folosind sockeți netlink.

Compilați cele două componente, copiați-le pe mașina virtuală QEMU, apoi inserați modulul de test și porniți utilitarul de user space.

Afișare PID-uri curente în /proc

O formă de configurare a nucleului și de comunicare în general din kernel space în user space îl reprezintă sistemele de fișiere virtuale sysfs și procfs. Acestea oferă o interfață similară unui sistem de fișiere clasic pe care utilizatorul o poate folosi.

În directorul proc/ din arhiva de suport a capitolului găsiți o implementare de modul de kernel care expune în fișierul virtual /proc/process_pids PID-urile proceselor curente din sistem.

Pentru testarea acestei componente, compilați modulul de kernel, apoi copiați-l în mașina virtuală QEMU, încărcați-l în kernel și apoi citiți conținutul fișierului virtual folosind comanda

cat /proc/process_pids

La descărcarea modulului din kernel, intrarea aferentă din /proc este ștearsă în mod automat.

Afișare informații despre procese în /proc

Actualizați modulul de kernel de mai sus pentru a afișa în intrarea /proc/current_maps informații despre zonele de memorie ale procesului curent.

Adică pentru procesul curent să afișați adresa de stare și adresa de sfârșit a fiecărei zone de memorie virtuală (definite de structura vm_area_struct).