This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
linux-kernel-dev:capitole:capitol-02 [2015/09/08 10:30] razvan [Modul din surse multiple] |
linux-kernel-dev:capitole:capitol-02 [2016/08/30 12:18] (current) razvan [Informații cscope] |
||
---|---|---|---|
Line 14: | Line 14: | ||
===== Resurse utile ===== | ===== Resurse utile ===== | ||
+ | |||
+ | * [[http://www.tldp.org/LDP/lkmpg/2.6/html/x323.html|The Linux Kernel Module Programming Guide: Passing Command Line Arguments to a Module]] | ||
+ | * [[https://lwn.net/Kernel/LDD3/|Linux Device Drivers, 3rd Edition]] (free) | ||
+ | * [[https://lwn.net/images/pdf/LDD3/ch02.pdf|Linux Device Drivers, 3rd Edition, Chapter 2: Building and Running Modules]] (free, PDF) | ||
+ | * [[http://lxr.free-electrons.com/|Linux Cross Reference (LXR) (Free Electrons)]] | ||
===== Exerciții ===== | ===== Exerciții ===== | ||
Line 108: | Line 113: | ||
Actualizați modulul ''hello.c'' pentru a afișa un mesaj și la descărcarea din kernel, la operația ''rmmod''. | 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 funcția ''printk'' astfel încât mesajele să fie afișate atât în bufferul kernel-ului (adică să fie afișate cu ''dmesg'') cât și la consolă. |
<note tip> | <note tip> | ||
Line 122: | Line 127: | ||
</note> | </note> | ||
+ | <note tip> | ||
+ | Pentru a include conținutul fișierului header ''include/linux/sched.h'' folosiți construcția | ||
+ | <code> | ||
+ | #include <linux/sched.h> | ||
+ | </code> | ||
+ | La fel veți proceda și în cazul altor fișiere de tip header în care sunt definite structuri, tipuri de date sau macro-uri pe care le folosiți în modulul vostru. | ||
+ | </note> | ||
==== Afișare comandă ==== | ==== Afișare comandă ==== | ||
Line 145: | Line 157: | ||
<note tip> | <note tip> | ||
- | Pentru testare folosiți ''ping'' din mașina virtuală VirtualBox către mașina virtuală QEMU. Și apoi pe mașina virtuală QEMU încărcați și descărcați modulul ''hook.ko''. | + | Pentru testare folosiți ''ping'' din mașina virtuală VirtualBox către mașina virtuală QEMU<code> |
+ | ping 172.20.0.2 | ||
+ | </code> | ||
+ | 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. | ||
</note> | </note> | ||
==== Modul din surse multiple ==== | ==== Modul din surse multiple ==== | ||
Line 156: | Line 177: | ||
Urmăriți fișierele intermediare și fișierul modul final. | 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 încarcați și descărcați modululul din kerkel. | + | 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 ==== | ==== 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țilizare și ieșire ale modulului. În celelalt 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. | + | 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 în cadrul funcției de inițializare a acestuia. |
Compilați modulul, copiați-l în mașina virtuală QEMU și testați-l. | Compilați modulul, copiați-l în mașina virtuală QEMU și testați-l. | ||
Line 169: | Line 190: | ||
<note tip> | <note tip> | ||
- | Pentru pachetele care pleacă folosiți ca ''hooknum'' valoarea ''NF_INET_LOCAL_OUT''. | + | Pentru pachetele care pleacă folosiți ca ''hooknum'' valoarea ''NF_INET_LOCAL_OUT''. Este vorba de câmpul ''hooknum'' din cadrul structura ''icmp_nf_ops''. |
+ | |||
+ | Pachetele de tipul ''echo reply'' au câmpul ''icmp_type'' al structurii ''icmp_hdr'' egal cu valoarea ''ICMP_ECHOREPLY''. | ||
+ | |||
+ | Puteți găsi definițiile de tipuri de câmpuri cu ajutorul ''cscope'' cu o comandă de genul<code> | ||
+ | vi -t ICMP_ECHO | ||
+ | </code> | ||
+ | </note> | ||
+ | |||
+ | ==== Informații cscope ==== | ||
+ | |||
+ | [[http://cscope.sourceforge.net/|Cscope]] este un program pentru parcurgerea eficientă a surselor C. Pentru a-l folosi, trebuie generată o bază de date cscope din sursele existente. Într-un tree Linux, este suficientă folosirea ''make ARCH=x86 cscope''. Precizarea arhitecturii prin variabila ARCH este opțională, dar recomandată; altfel, unele funcții dependente de arhitectură vor apărea de mai multe ori în baza de date. | ||
+ | |||
+ | Cscope poate fi folosit și stand-alone, dar este mult mai util în combinație cu un editor. Pentru a folosi cscope cu Vim, este necesar să instalați ambele pachete și să adăugați următoarele linii în fișierul ''.vimrc'' (mașina din laborator are deja configurările făcute): | ||
+ | |||
+ | <code vim> | ||
+ | if has("cscope") | ||
+ | " Look for a 'cscope.out' file starting from the current directory, | ||
+ | " going up to the root directory. | ||
+ | let s:dirs = split(getcwd(), "/") | ||
+ | while s:dirs != [] | ||
+ | let s:path = "/" . join(s:dirs, "/") | ||
+ | if (filereadable(s:path . "/cscope.out")) | ||
+ | execute "cs add " . s:path . "/cscope.out " . s:path . " -v" | ||
+ | break | ||
+ | endif | ||
+ | let s:dirs = s:dirs[:-2] | ||
+ | endwhile | ||
+ | |||
+ | set csto=0 " Use cscope first, then ctags | ||
+ | set cst " Only search cscope | ||
+ | set csverb " Make cs verbose | ||
+ | |||
+ | nmap <C-\>s :cs find s <C-R>=expand("<cword>")<CR><CR> | ||
+ | nmap <C-\>g :cs find g <C-R>=expand("<cword>")<CR><CR> | ||
+ | nmap <C-\>c :cs find c <C-R>=expand("<cword>")<CR><CR> | ||
+ | nmap <C-\>t :cs find t <C-R>=expand("<cword>")<CR><CR> | ||
+ | nmap <C-\>e :cs find e <C-R>=expand("<cword>")<CR><CR> | ||
+ | nmap <C-\>f :cs find f <C-R>=expand("<cfile>")<CR><CR> | ||
+ | nmap <C-\>i :cs find i ^<C-R>=expand("<cfile>")<CR>$<CR> | ||
+ | nmap <C-\>d :cs find d <C-R>=expand("<cword>")<CR><CR> | ||
+ | |||
+ | " Open a quickfix window for the following queries. | ||
+ | set cscopequickfix=s-,c-,d-,i-,t-,e-,g- | ||
+ | endif | ||
+ | </code> | ||
+ | |||
+ | Script-ul caută un fișier numit ''cscope.out'' în directorul curent, sau în directoarele părinte ale acestuia. Dacă Vim găsește acest fișier, puteți folosi combinația ''Ctrl+]'' sau ''Ctrl+\ g'' (combinația control-\, urmată de tasta g) pentru a sări direct la definiția cuvântului de sub cursor (funcție, variabilă, structură etc.). Similar, puteți folosi ''Ctrl+\ s'' pentru a merge la locurile unde este folosit cuvântul de sub cursor. | ||
+ | |||
+ | Puteți lua un fișier ''.vimrc'' cscope-enabled (and other goodies) de la [[https://github.com/ddvlad/cfg/blob/master/_vimrc]]. Următoarele indicații se bazează pe acest fișier, dar au listate și comenzile de bază vim care obțin același efect. | ||
+ | |||
+ | Dacă există mai multe rezultate (de obicei există) vă puteți deplasa între ele folosind ''F6'' și ''F5'' ('':cnext'' și '':cprev'') sau deschizând o subfereastră nouă cu rezultatele, folosind '':copen''. Ca să închideți subfereastra folosiți comanda '':cclose''. | ||
+ | |||
+ | Pentru a vă întoarce la locația precedentă, folosiți ''Ctrl+o'' (litera o, nu cifra zero). Comanda poate fi invocată de mai multe ori și funcționează chiar dacă cscope a schimbat fișierul pe care îl editați. | ||
+ | |||
+ | Pentru a merge la definiția unui simbol direct când porniți vim, folosiți ''vim -t task_struct''. Sau, dacă ați deschis Vim și vreți ulterior să căutați un simbol după nume, puteți folosi comanda '':cs find g <symbol_name>'' (unde ''<symbol_name>'' este numele simbolului. | ||
+ | |||
+ | Dacă ați găsit mai multe match-uri și dacă ați deschis o subfereastră cu toate match-urile (folosind '':copen'') și dacă sunteți în căutarea unui simbol de tip structură, este indicat să căutați în subfereastră (folosind ''/'' -- //slash//) caracterul ''{'' (acoladă deschisă). | ||
+ | |||
+ | <note important> | ||
+ | Un sumar al comenzilor ''cscope'' îl puteți obține folosind '':cs help''. | ||
+ | |||
+ | Pentru mai multe informații, folosiți help-ul integrat al Vim: '':h cscope'' sau '':h copen''. | ||
+ | </note> | ||
+ | |||
+ | Dacă sunteți utilizatori emacs, [[http://www.emacswiki.org/emacs/CScopeAndEmacs|wiki-ul emacs]] conține informații relevante pentru configurarea cscope. | ||
+ | |||
+ | Pentru o interfață mai simplă, [[http://sourceforge.net/projects/kscope/|Kscope]] este un frontend pentru cscope care foloseşte QT. Este lightweight, foarte rapid și foarte ușor de folosit. Permite căutare folosind expresii regulate, grafuri de apel etc. Kscope nu mai este, în momentul de fața, menținut. Există şi un [[https://opendesktop.org/content/show.php/Kscope4?content=156987|port]] al versiunii 1.6 pentru Qt4 şi KDE 4 care păstrează integrarea editorului Kate şi este mai uşor de folosit decât ultima versiune prezentă pe SourceForge. | ||
+ | |||
+ | <note important> | ||
+ | Dacă nu există deja un fișier ''cscope.out'' generat sau dacă s-a stricat, îl puteți genera folosind | ||
+ | <code> | ||
+ | make ARCH=x86 cscope | ||
+ | </code> | ||
+ | </note> | ||
+ | ==== cscope spelunking ==== | ||
+ | |||
+ | Folosiți direct Vim și comenzile cscope pentru parcurgerea codului sursă cu indicațiile de mai jos. | ||
+ | |||
+ | Determinați fișierul în care sunt definite următoarele tipuri de date: | ||
+ | * ''struct task_struct'' | ||
+ | * ''struct semaphore'' | ||
+ | * ''struct list_head'' | ||
+ | * ''spinlock_t'' | ||
+ | * ''struct file_system_type'' | ||
+ | |||
+ | <note tip> | ||
+ | Pentru o structură se caută doar numele ei. Spre exemplu, în cazul ''struct task_struct'' se caută șirul ''task_struct''. | ||
+ | |||
+ | De obicei veți obține mai multe match-uri caz în care: | ||
+ | - Listați toate match-urile folosind, în Vim, comanda '':copen''. Vă apare o fereastră secundară cu toate match-urile. | ||
+ | - Căutați match-ul potrivit (în care este definită structura) căutând după acoladă deschisă (''{''), un caracter sigur pe linia de definire a structurii. Pentru căutarea acoladei deschise folosiți, în Vim, construcția ''/{''. | ||
+ | - Pe linia aferentă apăsați ''Enter'' ca să vă ajungă editorul în codul sursă unde e definită variabila. | ||
+ | - Închideți fereastra secundară folosind coamanda '':cclose''. | ||
+ | </note> | ||
+ | |||
+ | Determinați fișierul în care sunt declarate următoarele variabile globale la nivelul nucleului: | ||
+ | * ''sys_call_table'' | ||
+ | * ''file_systems'' | ||
+ | * ''current'' | ||
+ | * ''chrdevs'' | ||
+ | |||
+ | <note tip> | ||
+ | Pentru aceasta folosiți în Vim o comandă de forma '':cs f g <symbol>'' (unde construcția ''<symbol>'' reprezintă numele simbolului căutat). | ||
+ | </note> | ||
+ | |||
+ | Determinați fișierul în care sunt declarate următoarele funcții: | ||
+ | * ''copy_from_user'' | ||
+ | * ''vmalloc'' | ||
+ | * ''schedule_timeout'' | ||
+ | * ''add_timer'' | ||
+ | |||
+ | <note tip> | ||
+ | Pentru aceasta folosiți în Vim o comandă de forma '':cs f g <symbol>'' (unde construcția ''<symbol>'' reprezintă numele simbolului căutat). | ||
+ | </note> | ||
+ | |||
+ | Parcurgeți secvența de structuri: | ||
+ | - ''struct task_struct'' | ||
+ | - ''struct mm_struct'' | ||
+ | - ''struct vm_area_struct'' | ||
+ | - ''struct vm_operations_struct'' | ||
+ | Adică parcurgeți din aproape în aproape structurile: accesați o structură și apoi găsiți câmpuri cu tipul de date al următoarei structuri, accesați-o pe aceasta etc. Rețineți în ce fișiere sunt definite; o să vă fie utile la alte laboratoare. | ||
+ | |||
+ | <note tip> | ||
+ | Pentru a căuta un simbol în Vim (cu suport ''cscope'') atunci când sunteți plasați cu cursorul pe acesta, folosiți construcția ''Ctrl+]''. | ||
+ | |||
+ | Pentru a reveni în match-ul anterior (înante de căutare/salt) folosiți construcția ''Ctrl+o''. Pentru a avansa în căutare (pentru a reveni la match-urile de dinainte de ''Ctrl+o'') folosiți construcția ''Ctrl+i''. | ||
+ | </note> | ||
+ | |||
+ | La fel ca mai sus, parcurgeți secvența de apeluri de funcții: | ||
+ | - ''bio_alloc'' | ||
+ | - ''bio_alloc_bioset'' | ||
+ | - ''bvec_alloc'' | ||
+ | - ''kmem_cache_alloc'' | ||
+ | - ''slab_alloc'' | ||
+ | |||
+ | <note tip> | ||
+ | Aveți în vedere indicațiile din secțiunea [[#informatii-cscope|Informații cscope]] de mai sus. | ||
</note> | </note> | ||
==== [BONUS] Afișarea unui șir primit ca parametru pentru modul ==== | ==== [BONUS] Afișarea unui șir primit ca parametru pentru modul ==== | ||
Line 176: | Line 334: | ||
<note tip> | <note tip> | ||
- | Inidicații sunt în directorul ''cap-02-doc/'' din arhiva capitolului. | + | Indicații sunt în directorul ''cap-02-doc/'' din arhiva capitolului și la link-urile din [[#resurse-utile|secțiunea de resurse]]: |
+ | * [[http://www.tldp.org/LDP/lkmpg/2.6/html/x323.html|The Linux Kernel Module Programming Guide: Passing Command Line Arguments to a Module]] | ||
+ | * [[https://lwn.net/images/pdf/LDD3/ch02.pdf|Linux Device Drivers, 3rd Edition, Chapter 2: Building and Running Modules]], secțiunea ''Module Parameters'' | ||
</note> | </note> | ||