Pentru a îmbunătăți cursul de SO, componentele sale și modul de desfășurare, ne sunt foarte utile opiniile voastre. Pentru aceasta, vă rugăm să accesați și completați formularul de feedback de pe site-ul curs.upb.ro. Trebuie să fiți autentificați și înrolați în cadrul cursului.
Formularul este anonim și este activ în perioada 23 mai 2022 - 3 iunie 2022. Rezultatele vor fi vizibile în cadrul echipei cursului doar după încheierea sesiunii. Este accesibil la link-ul “Formular feedback” a paginii principale a cursului de SO al seriei voastre pe curs.upb.ro. Nu este în meta-cursul disponibil tuturor seriilor.
Vă invităm să evaluați activitatea echipei de SO și să precizați punctele tari și punctele slabe și sugestiile voastre de îmbunătățire a disciplinei. Feedback-ul vostru ne ajută să creștem calitatea materiei în anii următori și să îmbunătățim disciplinele pe care le veți face în continuare.
Vom publica la începutul semestrului viitor analiza feedback-ului vostru.
Ne interesează în special:
Nucleul gestionează fiecare device hardware sau virtual prin intermediul unui device driver. Un device driver este o porțiune de cod din nucleu care implementează o serie de operații corespunzătoare acțiunilor de I/E (intrare/ieşire) asociate cu un device hardware. Procesele din userspace (spațiul utilizator) interacționează cu device driver-ul prin intermediul unor fișiere speciale denumite device nodes. API-ul (interfața de programare) oferit de device drivere include, în general, următoarele operații:
open
,close
,read
,write
,ioctl
.Operaţiile de mai sus sunt aplicate asupra device node-ului sau descriptorului de fişier asociat acelui device node.
Unele device-uri sunt reale (mouse, tastatură, disc), altele sunt virtuale în sensul că nu au un device hardware asociat (e.g /dev/zero
, /dev/null
). După modul în care se accesează datele, device-urile sunt împărțite în două categorii:
device de tip caracter
, datele sunt procesate octet cu octet. În această categorie se înscriu: tastatura, linia serială, mouse-ul.device de tip bloc
, datele pot fi procesate la nivel de bloc, e.g. hard disk.
Fișierele device node se găsesc în /dev
și au asociat un identificator format din major ID și minor ID.
Majorul și minorul sunt dați de coloanele 5 și 6 din output-ul ls -l
, separate prin virgulă.
Puteți vizualiza majorii folosiți în sistem din fișierul /proc/devices
.
Crearea unui device node se face folosind funcția mknod
int mknod(const char *pathname, mode_t mode, dev_t dev);
În general, informații despre fișiere și în particular despre device node-uri se pot afla cu funcțiile din familia stat.
int stat(const char *path, struct stat *buf); int fstat(int fd, struct stat *buf); int lstat(const char *path, struct stat *buf);
Toate aceste funcții completează informațiile despre un fișier în structura struct stat
, care conține următoarele câmpuri:
struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* inode number */ mode_t st_mode; /* protection */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* total size, in bytes */ blksize_t st_blksize; /* blocksize for file system I/O */ blkcnt_t st_blocks; /* number of 512B blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last status change */ };
Un sistem de fișiere este o colecție organizată de fișiere și directoare. Un sistem de fișiere este creat folosind comanda mkfs. Din punct de vedere funcțional sistemele de fișiere se pot împărți în:
Tipurile de sisteme de fișiere suportate de nucleu pot fi observate în fișierul /proc/filesystems
.
daniel@debian$ cat /proc/filesystems nodev sysfs nodev proc nodev ramfs ext4 fuseblk
Pentru a putea fi folosit un sistem de fișiere trebuie atașat (montat) în ierarhia de directoare din sistem. Acest lucru se realizează cu comanda mount(8):
mount -t type device dir
sau apelul mount(2):
int mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data);
Operația inversă, demontarea sistemului de fișiere din ierarhia de directoare se face cu comanda umount(8):
umount {dir|device}...
sau apelul: umount(2)
int umount(const char *target);
Fiecare proces are două atribute legate de directoare:
Un director este stocat în sistemul de fișiere într-un mod similar cu un fișier obișnuit. Există două lucruri diferite:
Un link simbolic (sau soft link), este un tip special de fișier al cărui conținut reprezintă numele altui fișier. Link-urile simbolice sunt create cu comanda ln -s
sau cu apelul symlink(2)
int symlink(const char *oldpath, const char *newpath);
Ștergerea unui link simbolic se face cu comanda unlink
sau cu apelul unlink(2)
int unlink(const char *pathname);
Un director poate fi creat folosind comanda mkdir
sau apelul mkdir(2))
int mkdir(const char *pathname, mode_t mode);
Apelul rmdir(2) șterge directorul specificat în argumentul pathname
:
int rmdir(const char *pathname);
De asemenea, pentru a șterge un fișier sau un director gol se poate folosi funcția remove(3)
int remove(const char *pathname);
După cum am precizat mai sus, un director conține nume de directoare sau fișiere.
Apelul opendir(3) deschide un director și întoarce un handle ce poate fi folosit mai târziu pentru a referi directorul.
DIR *opendir(const char *name); DIR *fdopendir(int fd);
Apelul readdir(3) citește intrări succesive dintr-un stream de directoare (DIR).
struct dirent *readdir(DIR *dirp);
Apelul readdir
întoarce un pointer la următoarea structură struct dirent
din streamul referit de dir
:
struct dirent { ino_t d_ino; /* inode number */ off_t d_off; /* offset to the next dirent */ unsigned short d_reclen; /* length of this record */ unsigned char d_type; /* type of file; not supported by all file system types */ char d_name[256]; /* filename */ };
Directorul curent al unui proces definește punctul de start pentru formarea căilor relative referite de procesul respectiv. Un proces nou creat moștenește directorul curent de la procesul părinte.
Directorul curent al unui proces poate fi determinat folosind apelul getcwd(3):
char *getcwd(char *cwdbuf, size_t size);
cwdbuf
, trebuie alocat înainte de apel astfel încât să poată stoca cel puțin size
octeți.cwdbuf
va conține calea absolută a directorului curent.Apelul chdir(2) schimbă directorul curent al procesului apelant către numele absolut sau relativ primit ca argument.
int chdir(const char *path);
Fiecare proces are un director rădăcină reprezentând punctul de unde căile absolute sunt interpretate. În mod implicit, acesta este directorul rădăcina real al sistemului de fișiere. Un proces nou moștenește directorul rădăcină de la părintele său. Există situații (e.g pentru a ascunde o parte din sistemul de fișiere) în care este util pentru un proces să-și schimbe directorul rădăcină. Acest lucru se realizează folosind apelul chroot(2)
int chroot(const char *path);
Apelul realpath(3) dereferențiază link-ul simbolic primit ca argument în path
și elimină subşirurile /./
, /../
precum şi apariţii în plus ale caracterului /
pentru a genera o cale absolută plasată în parametrul de ieşire realpath
.
Exemplu: Dacă parametrul de intrare path
ar fi /home/madalina/////so/tema4/../../seriale/thewire/
, în urma apelului realpath(path, resolved_path)
, parametrul de ieşire resolved_path
devine /home/madalina/seriale/thewire/
. Presupunem că ierarhia de directoare prezentată în exemplu există deja.
char *realpath(const char *path, char *resolved_path);
Apelurile dirname(3) și basename(3) împart un șir de caractere reprezentând o cale în partea de director
și partea de fișier.
char *dirname(char *path); char *basename(char *path);
De exemplu:
path dirname basename "/usr/lib" "/usr" "lib" "/usr/" "/" "usr" "usr" "." "usr" "/" "/" "/" "." "." "." ".." "." ".."
git clone https://github.com/systems-cs-pub-ro/so
. Dacă doriți să descărcați repositoryul în altă locație, folosiți comanda git clone https://github.com/systems-cs-pub-ro/so ${target}
.
Pentru a actualiza repository-ul, folosiți comanda git pull origin master
din interiorul directorului în care se află repository-ul. Recomandarea este să îl actualizați cât mai frecvent, înainte să începeți lucrul, pentru a vă asigura că aveți versiunea cea mai recentă. În cazul în care gitul detectează conflicte la nivelul vreunui fişier, folosiți următoarele comenzi pentru a vă păstra modificările:
git stash git pull origin master git stash pop
Pentru mai multe informații despre folosirea utilitarului git, urmați ghidul de la https://gitimmersion.com.
Vă invităm să evaluați activitatea echipei de SO și să precizați punctele tari și punctele slabe și sugestiile voastre de îmbunătățire a materiei. Feedback-ul vostru este foarte important pentru noi să creștem calitatea materiei în anii următori și să îmbunătățim materiile pe care le veți face în continuare.
mini.c
din directorul 1-mini/
. Pentru fiecare exercițiu decomentați linia TODO
corespunzătoare.
Folosiți comanda ls -l /dev
și precizați două device node-uri de tip caracter și două device node-uri de tip bloc. Ce major și minor au?
Implementați comanda list
<device_node>, ce va primi ca argument un device node și va afișa pentru acesta tipul (c/b), identificatorii major
, respectiv minor
. Folosiți funcția stat(2) pentru a obține o structură de tipul struct stat
din care veți extrage tipul device-ului (st_mode
) (hint: S_ISCHR, S_ISBLK) apoi din câmpul st_rdev
extrageți major și minor. Nu uitați să decomentați linia marcată cu #define TODO2
Creați punctul de montare /mnt/my
. Ca root, în terminalul bash, rulați comanda:
$ mkdir /mnt/my
Parcurgeți paginile de manual ale funcțiilor mount și umount.
Folosiți comenzile mount
și umount
din executabilul mini pentru a monta fişierul my_fs
în punctul de montare /mnt/my
. Citiți secțiunea marcată cu TODO
din fișierul mini.c
. Pentru argumentul 4 și argumentul 5 al funcției mount
folosiți, respectiv, valorile 0
și NULL
.
Testare: Rulați, ca root, comanda:
./mini
și apoi rulați comanda de montare în cadrul acestui shell:
mount /dev/mapper/vgvagrant-root /mnt/my ext4
În cazul în care nu lucraţi în maşina virtuală de SO, folosiţi /dev/sda1
în loc de /dev/mapper/vgvagrant-root
.
Într-o altă consolă, într-un shell obișnuit, verificați rezultatele folosind comanda:
cat /proc/mounts
Pentru demontare rulați comanda:
umount /mnt/my
Adăugați suport pentru comenzile symlink și unlink în programul mini
. Urmăriți TODO4 .
Pentru testare folosiți, în shell-ul aferent comenzii ./mini
, comanda:
symlink /proc/filesystems local-filesystems
Ca să verificați, într-o altă consolă, în același director cu cel în care ați rulat comanda ./mini
, folosiți
ls -l
Pentru a șterge symlink-ul folosiți comanda
unlink local-filesystems
Pentru validare rulați din nou comanda
ls -l
Adăugați suport pentru comenzile mkdir și rmdir în programul mini
. Urmăriți TODO5 .
Ca al doilea argument pentru funcția mkdir
folosiți (mode_t) 0755
.
Adăugați suport pentru comanda ls dirname/
în programul mini
. Aceasta va trebui să afișeze recursiv toate directoarele și fișierele începând cu directorul dat ca parametru (puteți parcurge recursiv în adâncime arborele de fișiere). Urmăriți TODO6 și demo-ul 5 de la curs
Adăugați suport pentru comenzile pwd și chdir în programul mini
. Urmăriți TODO7 .