====== 4. Comunicarea între spațiul utilizator și spațiul kernel ====== ===== Subiecte abordate ===== * Nevoia de comunicare între spațiul utilizator și spațiul kernel * Forme de comunicare: notificare (semnalizare) și transfer de informație * Diferența între IPC și comunicare user space - kernel space * Folosirea dispozitivelor pentru comunicare (read, write, ioctl) * Maparea memoriei * Sockeți netlink * Folosirea procfs pentru comunicare ===== Resurse utile ===== * http://people.ee.ethz.ch/~arkeller/linux/kernel_user_space_howto.html * http://man7.org/linux/man-pages/man7/netlink.7.html * http://www.linuxfoundation.org/collaborate/workgroups/networking/generic_netlink_howto * http://binwaheed.blogspot.ro/2010/08/after-reading-kernel-source-i-finally.html * http://tuxthink.blogspot.ro/2013/10/creating-read-only-proc-entry-in-kernel.html * http://pointer-overloading.blogspot.ro/2013/09/linux-creating-entry-in-proc-file.html ===== Exerciții ===== Arhiva de suport pentru exerciții se găsește [[http://koala.cs.pub.ro/training/res/linux-kernel-dev/arc/cap-04-tasks.zip|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/'' 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 [[:linux-kernel-dev:capitole:capitol-03|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. ==== Comunicare cu socket netlink ==== 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 [[http://man7.org/linux/man-pages/man7/netlink.7.html|sockeți netlink]] așa cum este folosit în [[http://man7.org/linux/man-pages/man7/rtnetlink.7.html|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'').