Laborator 8: Exerciții

Pentru desfășurarea laboratorului pornim de la arhiva de sarcini a laboratorului. Descărcăm și decomprimăm arhiva în directorul so2/ din directorul home al utilizatorului student de pe sistemul de bază (stația mjolnir):

student@mjolnir:~$ cd so2/
student@mjolnir:~/so2$ wget http://elf.cs.pub.ro/so2/res/laboratoare/lab08-tasks.zip
student@mjolnir:~/so2$ unzip lab08-tasks.zip
student@mjolnir:~/so2$ tree lab08-tasks

În cadrul directorului lab08-tasks/ se găsesc resursele necesare pentru dezvoltarea exercițiilor de mai jos: fișiere schelet de cod sursă, fișiere Makefile și Kbuild, scripturi și programe de test.

Vom dezvolta exercițiile pe sistemul de bază (stația mjolnir) și apoi le vom testa pe mașina virtuală QEMU. După editarea și compilarea unui modul de kernel îl vom copia în directorul dedicat pentru mașina virtuală QEMU folosind o comandă de forma

student@mjolnir:~/so2$ cp /path/to/module.ko ~/so2/qemu-so2/fsimg/root/modules/

unde /path/to/module.ko este calea către fișierul obiect aferent modulului de kernel. Apoi vom porni, din directorul ~/so2/qemu-so2/ mașina virtuală QEMU folosind comanda

student@mjolnir:~/so2/qemu-so2$ make

După pornirea mașinii virtuale QEMU vom putea folosi comenzi în fereastra QEMU pentru a încărca și descărca modulul de kernel:

# insmod modules/module-name.ko
# rmmod module/module-name

unde module-name este numele modulului de kernel.

Pentru dezvoltarea laboratorului, este recomandat să folosim trei terminale sau, mai bine, trei tab-uri de terminal. Pentru a deschide un nou tab de terminal folosim combinația de taste Ctrl+Alt+t. Cele trei tab-uri de terminal îndeplinesc următoarele roluri:

  1. În primul tab de terminal dezvoltăm modulul de kernel: editare, compilare, copiere în directorul dedicat pentru mașina virtaulă QEMU. Lucrăm în directorul aferent rezultat în urma decomprimării arhivei de sarcini a laboratorului.
  2. În al doilea tab de terminal pornim mașina virtuală QEMU și apoi testăm modulul de kernel: încărcare/descărcare modul, rulare teste. Lucrăm în directorul aferent mașinii virtuale: ~/so2/qemu-so2/.
  3. În al treilea tab de terminal pornim minicom sau un server UDP care să primească mesajele de netconsole. Nu contează în ce director ne aflăm. Folosim comanda
    student@mjolnir:~$ netcat -lup 6666

[4.5p] myfs

Pentru început ne propunem să ne acomodăm cu interfața expusă de nucleul Linux și de componenta VFS (Virtual File System). De aceea, pentru început vom lucra cu un sistem de fișiere simplu, virtual (adică fără suport fizic pe disc). Sistemul de fișiere poartă denumirea myfs.

Pentru aceasta vom accesa subdirectorul myfs/ din arhiva de sarcini a laboratorului. Vom implementa în cadrul acestui laborator operațiile pe superbloc, iar laboratorul viitor vom continua cu operațiile pe inode.

1. [1.5p] Înregistrare și deînregistrare sistem de fișiere myfs

Primul pas în lucrul cu sistemul de fișiere este înregistrea și deînregistrarea acestuia. Dorim să facem acest lucru pentru sistemul de fișiere descris în fișierul myfs.c. Parcurgeți conținutul fișierului și urmăriți indicațiile marcate cu TODO 1.

Pașii pe care trebuie să-i realizați sunt descriși în secțiunea Înregistrarea și deînregistrarea sistemelor de fișiere. Folosiți șirul "myfs" pentru numele sistemului de fișiere.

În cadrul structurii de sistem de fișiere, pentru completarea superbloculului (efectuată la montare) folosiți funcția myfs_mount, prezentă în scheletul de cod. În cadrul funcției myfs_mount apelați funcția specifică unui sistem de fișiere fără suport pe disc (vedeți secțiunea Funcțiile mount, kill_sb; este vorba de funcția mount_nodev). Ca argument pentru funcția de mount specifică folosiți funcția de tipul funcția fill_super definită în scheletul de cod.

Pentru distrugerea superblocului (efectuată la demontare) folosiți funcția kill_litter_super, funcție specifică, de asemenea, unui sistem de fișiere fără suport pe disc.

După completarea secțiunilor marcate cu TODO 1, compilați modulul, copiați-l în directorul dedicat pentru mașina virtuală QEMU și porniți mașina virtuală. Încărcați modulul în kernel și apoi verificați prezența sistemului de fișiere myfs în cadrul fișierului /proc/filesystems.

Pe moment sistemul de fișiere este doar înregistrat, nu expune operații de utilizare a acestuia. Dacă încercăm să-l montăm, operația va eșua. Pentru a încerca montarea, creăm punctul de montare /mnt/myfs/

# mkdir -p /mnt/myfs

și apoi folosim comanda mount

# mount -t myfs none /mnt/myfs

Mesajul de eroare obținut ne arată că nu avem implementate operațiile de lucru pe superbloc. Va trebui să implementăm operațiile de lucru pe superbloc și să inițializăm inode-ul rădăcină. Vom face aceste lucru în continuare.

Argumentul none trimis comenzii mount indică faptul că nu avem un dispozitiv de pe care montăm, sistemul de fișiere fiind unul virtual. În mod similar sunt montate sistemele de fișiere procfs sau sysfs pe sistemele Linux.

2. [1p] Completare superbloc myfs

Pentru a putea monta sistemul de fișiere, trebuie să completăm superblocul acestuia, adică o structură generică din VFS de tipul struct super_block. Completarea structurii o vom face în cadrul funcției myfs_fill_super; este vorba de variabila sb transmisă ca argument funcției. Urmăriți indicațiile marcate cu TODO 2.

Pentru completarea funcției myfs_fill_super puteți porni de la exemplul din secțiunea funcția fill_super.

Pentru câmpurile structurii de superbloc, acolo unde se poate, folosiți macro-urile definite în cadrul scheletului de cod.

Câmpul s_op al superblocului trebuie inițializat la structura de operații a superblocului de tipul struct super_operations. Trebuie să definiți o astfel de structură.

Informații despre definirea structurii struct super_operations și despre completarea superblocului se găsesc în secțiunea Operațiile pe superbloc.

Inițializați câmpurile drop_inode și statfs ale structurii struct super_operations.

Deși în acest moment superblocul va fi inițializat corespunzător, operația de mount va eșua în continuare. Pentru ca operația să fie definitivată cu succes, va trebui inițializat inode-ul rădăcină, lucru pe care îl vom facem la exercițiul următor.

3. [1.5p] Inițializare inode rădăcină myfs

Inode-ul rădăcină este inode-ul aferent directorului rădăcină al sistemului de fișiere (adică /). Inițializarea sa se face la montare. Funcția myfs_fill_super, apelată la montare, este cea care apelează funcția myfs_get_inode care creează și inițializează un inode. În mod obișnuit, funcția este folosită pentru crearea și inițializarea tuturor inode-urilor; în acest exercițiu, însă, vom crea doar inode-ul rădăcină.

Inode-ul este alocat în cadrul funcției myfs_get_inode. Este vorba de variabila locală inode, alocată cu ajutorul apelului new_inode.

Pentru a definitiva cu succes montarea sistemului de fișiere, va trebui să completați funcția myfs_get_inode. Urmăriți indicațiile marcate cu TODO 3. Un punct de plecare este funcția ramfs_get_inode.

Pentru inițializarea uid, gid, mode, puteți folosi funcția inode_init_owner așa cum este folosită și în funcția ramfs_get_inode. Când apelați funcția inode_init_owner folosiți NULL ca al doilea parametru, întrucât nu există director părinte pentru inode-ul creat.

Inițializați câmpurile i_atime, i_ctime și i_mtime ale inode-ului VFS la valoarea CURRENT_TIME.

Va trebui să inițializați operațiile pentru inode-ul de tip director. Pentru aceasta urmați pașii:

  1. Verificați dacă este vorba de inode de tip director folosind macro-ul S_ISDIR.
  2. Pentru câmpurile, i_op și i_fop, folosiți funcţii din kernel deja implementate:
  3. Incrementați numărul de link-uri pentru director folosind funcția inc_nlink.

4. [0.5p] Testare montare și demontare myfs

Acum putem să montăm sistemul de fișiere. Urmați pașii indicați mai sus pentru a compila modulul de kernel, copia pe mașina virtuală și porni mașina virtuală și apoi inserați modulul de kernel, creați punctul de montare /mnt/myfs/ și montați sistemul de fișiere. Verificăm că sistemul de fișiere a fost montat investigând fișierul /proc/mounts.

Ce număr de inode are directorul /mnt/myfs? De ce?

Pentru a afișa numărul de inode al unui director folosiți comanda:

ls -di /path/to/directory

unde /path/to/directory/ este calea către directorul al cărui număr de inode vrem să-l afișăm.

Verificăm statisticile sistemului de fișiere myfs cu ajutorul comenzii:

stat -f /mnt/myfs

Vrem să vedem ce conține punctul de montare /mnt/myfs și dacă putem crea fișiere. Pentru aceasta rulăm comenzile:

# ls -la /mnt/myfs
# touch /mnt/myfs/a.txt

Observăm că nu putem crea fișierul a.txt în sistemul de fișiere. Acest lucru se întâmplă pentru că nu avem implementate operațiile de lucru cu inode-uri în structura struct super_operations. Vom implementa aceste operații laboratorul următor.

Demontăm sistemul de fișiere folosind comanda

umount /mnt/myfs

Descărcați și modulul de kernel aferent sistemul de fișiere.

Pentru testarea întregii funcționalități puteți folosi scriptul test-myfs.sh:

./test-myfs.sh

Statisticile afișate pentru sistemul de fișiere sunt minimale, întrucât informațiile sunt furnizate de către funcția simple_statfs.

[6.5p] minfs

În continuare vom implementa bazele unui sistem de fișiere foarte simplu, denumit minfs, cu suport pe disc. Vom folosi un disc din cadrul mașinii virtuale pe care îl vom formata și monta cu sistemul de fișiere minfs.

Pentru aceasta vom accesa directorul minfs/ din archiva de sarcini a laboratorului și vom folosi scheletul de cod minfs.c. La fel ca la myfs nu vom implementa operații de lucru cu inode-urile, limitându-ne doar la operațiile de lucru cu superblocul și, așadar, la montare. Restul operațiilor le vom implementa în laboratorul următor.

Urmăriți diagrama de mai jos pentru a clarifica rolul structurilor din cadrul sistemului de fișiere minfs.

5. [0.5p] Înregistrare și deînregistrare sistem de fișiere minfs

Pentru înregistrarea și deînregistrarea sistemului de fișiere va trebui să completați, în fișierul minfs.c, structura minfs_fs_type și funcția minfs_mount. Urmăriți indicațiile marcate cu TODO 1.

În cadrul structurii de sistem de fișiere, pentru montare folosiți funcția minfs_mount prezentă în scheletul de cod. În cadrul acestei funcții apelați funcția aferentă pentru montarea unui sistem de fișiere cu suport de disc (vedeți secțiunea Funcțiile mount, kill_sb; este vorba de funcția mount_bdev).

Alegeți funcția cea mai potrivită pentru distrugerea superblocului (efectuată la demontare); țineți cont de faptul că este un sistem de fișiere cu suport pe disc. Este vorba de funcția kill_block_super.

Inițializați câmpul fs_flags al structurii minfs_fs_type cu valoarea corespunzătoare pentru un sistem de fișiere cu suport pe disc. Vedeți secțiunea Înregistrarea și deînregistrarea sistemelor de fișiere.

Funcția de completare a superblocului este minfs_fill_super.

După completarea secțiunilor marcate cu TODO 1, compilați modulul, copiați-l în directorul dedicat pentru mașina virtuală QEMU și porniți mașina virtuală. Încărcați modulul în kernel și apoi verificați prezența sistemului de fișiere minfs în cadrul fișierului /proc/filesystems.

Pentru a putea testa montarea sistemului de fișiere va trebui să formatăm discul cu structura acestuia. Formatarea necesită utilitarul de formatare mkfs.minfs compilabil din fișierul mk_minfs.c folosind comanda

student@mjolnir:~so2/lab08-tasks/minfs$ make -f Makefile.format

În urma compilării rezultă executabilul mkfs.minfs care trebuie copiat, împreună cu modulul de kernel, în mașina virtuală.

După compilare, copiere și pornirea mașinii virtuale, formatăm discul /dev/vdb folosind utilitarul de formatare:

# ./mkfs.minfs /dev/vdb

Încărcăm modulul de kernel

# insmod minfs.ko

creăm punctul de montare /mnt/minfs/

# mkdir -p /mnt/minfs/

și montăm sistemul de fișiere

# mount -t minfs /dev/vdb /mnt/minfs/

Operaţia eşuează din cauză că inode-ul rădăcină nu este iniţializat.

6. [2.5] Completare superbloc minfs

Pentru a putea monta sistemul de fișiere va trebui să completați superblocul (adică o structură de tip struct super_block) în cadrul funcției minfs_fill_super; este vorba de argumentul s al funcției. Structura de operații pe superbloc este definită: minfs_ops. Urmăriți indicațiile marcate cu TODO 2. Puteți urmări implementarea funcției minix_fill_super.

Unele structuri se găsesc definite în fișierul header minfs.h.

Pentu informații legate de lucrul cu buffere, parcurgeți secțiunea Buffer cache-ul.

Citiți primul bloc de pe disc (blocul cu indexul 0). Pentru a citi blocul, folosiți funcția sb_bread. Convertiți datele citite (câmpul b_data din struct buffer_head) la structura de tip informație de superbloc de pe disc: struct minfs_super_block, definită în fișierul cod sursă.

Structura struct minfs_super_block deține informații specifice sistemului de fișiere, care nu se regăsesc în structura generică struct super_block (în cazul de față doar versiunea). Acele informații suplimentare (care se găsesc în struct minfs_super_block (disc) dar nu în struct super_block (VFS)) vor fi stocate în structura struct minfs_sb_info.

Pentru verificarea funcționalității avem nevoie de o funcţie pentru citirea inode-ului rădăcină. Pe moment folosiți funcția myfs_get_inode de la exercițiile cu sistemul de fișiere myfs. Copiați funcția în codul sursă şi apelaţi-o la fel ca în cazul myfs. Al doilea argument în cazul apelării funcției myfs_get_inode îl reprezintă permisiunile de creare ale inode-ului, similar exercițiului cu sistem de fișiere virtual (myfs).

Validaţi implementarea executând comenzile de la exerciţiul anterior.

7. [1.5p] Creare și distrugere inode-uri minfs

Pentru montare avem nevoie de inițializarea inode-ului rădăcină, iar pentru inițializarea inode-ului rădăcină avem nevoie de implementarea funcțiilor de lucru cu inode-uri. Adică trebuie să implementați funcțiile minfs_alloc_inode și minfs_destroy_inode. Urmăriții indicațiile marcate cu TODO 3. Puteți folosi ca model funcțiile minix_alloc_inode și minix_destroy_inode.

În implementare urmăriți macro-urile și structurile din fișierul minfs.h.

Pentru alocarea/dezalocarea de memorie în cadrul funcțiilor minfs_alloc_inode și minfs_destroy_inode recomandăm folosirea kzalloc și kfree.

În funcția minfs_alloc_inode alocați inode-uri de tip minfs_inode_info, dar returnați doar structuri de tip struct inode, adică cele date de câmpul vfs_inode.

În funcția de minfs_alloc_inode apelați funcția inode_init_once pentru inițializarea inode-ului.

În funcția destroy_inode. să puteți accesa structura de tip struct minfs_inode_info folosiți macro-ul container_of.

În acest exerciţiu aţi implementat funcţiile minfs_alloc_inode şi minfs_destroy_inode, dar ele nu sunt încă apelate. Corectitudinea implementării o veţi verifica la sfârşitul exerciţiului următor.

8. [1.5p] Inițializare inode rădăcină minfs

Inițializarea inode-ului rădăcină este necesară pentru montarea sistemului de fișiere. Pentru aceasta va trebui să completați structura minfs_ops cu funcțiile minfs_alloc_inode și minfs_destroy_inode și să completați funcția minfs_iget. Funcția minfs_iget este funcția apelată pentru alocarea unui inode de tip VFS (adică struct inode) și completarea acestuia cu informații specifice inode-ului minfs de pe disc (adică struct minfs_inode). Urmăriții indicațiile marcate cu TODO 4. Completați câmpurile alloc_inode și destroy_inode ale structurii struct super_operations cu funcțiile implementate la pasul anterior.

Informațiile despre inode-ul rădăcină se găsesc în al doilea bloc de pe disc (inode-ul cu indexul 1). Realizați, în cadrul funcției minfs_iget citirea inode-ului rădăcină de tip minfs de pe disc (struct minfs_inode) și completarea inode-ului VFS (struct inode).

În cadrul funcției minfs_fill_super înlocuiți apelul funcției myfs_get_inode cu apelul funcției minfs_iget.

Pentru implementarea funcției minfs_iget urmăriți implementarea funcției V1_minix_iget.

Pentru a citi un bloc folosiți funcția sb_bread. Convertiți datele citite (câmpul b_data al structurii struct buffer_head) la inode-ul de tip minfs de pe disc (struct minfs_inode).

Câmpurile i_uid, i_gid, i_mode, i_size le completați în cadrul inode-ului VFS cu valorile din inode-ul de pe disc. Pentru initializarea câmpurilor i_uid și i_gid, folosiți funcțiile i_uid_write, și, respectiv, i_gid_write.

Inițializați câmpurile i_atime, i_ctime și i_mtime ale inode-ului VFS la valoarea CURRENT_TIME.

Va trebui să inițializați operațiile pentru inode-ul de tip director. Pentru aceasta urmați pașii:

  1. Verificați dacă este vorba de inode de tip director folosind macro-ul S_ISDIR.
  2. Pentru câmpurile, i_op și i_fop, folosiți funcții din kernel deja implementate:
  3. Incrementați numărul de link-uri pentru director folosind funcția inc_nlink.

9. [0.5p] Testare montare și demontare minfs

Acum putem să montăm sistemul de fișiere. Urmați pașii indicați mai sus pentru a compila modulul de kernel, copia pe mașina virtuală și porni mașina virtuală și apoi inserați modulul de kernel, creați punctul de montare /mnt/minfs/ și montați sistemul de fișiere. Verificăm că sistemul de fișiere a fost montat investigând fișierul /proc/mounts.

Verificăm că totul este în regulă prin listarea conținutului punctului de montare /mnt/minfs/:

# ls /mnt/minfs/

După montare și verificare, demontăm sistemul de fișiere și descărcăm modulul din kernel.

Alternativ, pentru testarea întregii funcționalității puteți folosi scriptul test-minfs.sh:

# ./test-minfs.sh

Soluții

so2/laboratoare/lab08/exercitii.txt · Last modified: 2017/04/11 12:48 by razvan.deaconescu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0