Chiamate di sistema Introduzione Errori : perror() Chiamate che lavorano su file 1 Chiamate di sistema • Sappiamo bene cosa sono …. • Dal C è possibile invocare le chiamate di sistema POSIX utilizzando la libreria standard – header vari da includere : unistd.h, sys/types.h, sys/wait.h etc... • Queste informazioni tipicamente si ricavano dai manuali in linea – es. man 2 fork 2 Manuali in linea ... • Tipico formato : NAME perror - print a system error msg SYNOPSIS include,prototipi, globali DESCRIPTION descrizione a parole CONFORMING TO standard ... SEE ALSO funzioni collegate 3 Manuali in linea …(2) • Ci sono 3 sezioni : – 1 (default) le utility chiamabili da shell – 2 le system call – 3 le funzioni di libreria standard C • Ci sono utility che hanno lo stesso nome delle funzioni nelle sezioni 2/3, – specificare la sezione per avere l’informazione corretta • Se non funzionano: – controllare il valore della variabile di ambiente MANPATH 4 UNIX: struttura generale Utenti Interfaccia di libreria C Interfaccia delle chiamate di sistema Programmi di utilità standard (shell, editori, compilatori etc.) Modo utente Libreria standard (Open, close, read, write …) Sistema operativo Unix (gestione processi, memoria, file system, I/0..) Hardware Modo kernel 5 Chiamate di sistema: errori • Le chiamate di sistema possono fallire – in caso di fallimento ritornano un valore diverso da 0 (tipicamente -1) – il codice relativo all’errore rilevato è inserito nella variabile globale errno (errno.h) – i codici di errore sono definiti in vari file di include – perror() routine della libreria standard che stampa i messaggi di errore relativi a diversi codici (stdio.h) 6 Chiamate di sistema: errori (2) • Esempi di codici di errore /* no such file or directory*/ #define ENOENT 2 /* I/O error*/ #define EIO 5 /* Operation not permitted */ #define EPERM 1 7 Chiamate di sistema: errori (3) • Come funziona perror(“pippo”) – legge il codice di errore contenuto nella globale errno – stampa “pippo” seguito da “:” seguito dal messaggio di errore relativo al codice – uso tipico : perror(“fun, descr”) dove fun è il nome della funzione che ha rilevato l’errore, descr descrive cosa stiamo tentando di fare – la stampa viene effettuata sullo standard error del processo in esecuzione (tipic. schermo) 8 Chiamate di sistema: errori (4) • Es. int main (void) { errno = 1; /* EPERM */ perror(“main, provaerr”); return 0; } • Compilato ed eseguito ….. $ a.out main, provaerr : Operation not permitted $ 9 Chiamate di sistema: errori (5) • Errno viene sovrascritto dalla SC successiva (se erronea) • Il programma deve controllare l’esito di ogni SC immediatamente dopo il ritorno ed agire • L’azione minima è chiamare la perror() per stampare un messaggio di errore • Nel corso utilizzeremo delle macro con parametri che inseriscono test e perror() ad ogni chiamata 10 In sysmacro.h /* stampa errore e termina */ #define IFERROR(s,m) \ if((s)==-1) {perror(m); exit(errno);} /* stampa errore ed esegue c */ #define IFERROR3(s,m,c) \ if((s)==-1) {perror(m); c;} 11 In sysmacro.h (2) #define IFERROR(s,m) \ if((s)==-1) {perror(m); exit(errno);} #define IFERROR3(s,m,c) \ if((s)==-1) {perror(m); c;} /* uso tipico */ int main (void) { IFERROR3(read(…),”main, des”, return -1); IFERROR(read(…),”main, des”); } 12 SC che operano su file (1) open(), read(), write(), close() 13 Apertura di un file : SC open() int open(const char * pathname, int flags) – pathname : PN relativo o assoluto del file – flags : indicano come voglio accedere al file • O_RDONLY sola lettura, O_WRONLY sola scrittura, O_RDWR entrambe • eventualmente messe in or bit a bit una o più delle seguenti maschere : O_APPEND scrittura in coda al file, O_CREAT se il file non esiste deve essere creato, O_TRUNC in fase di creazione, se il file esiste viene sovrascritto, O_EXCL in fase di creazione, se il file esiste si da errore 14 Apertura di un file : SC open() (2) int open(const char * pathname, int flags) – risultato : un intero, il descrittore di file (fd) Tabella dei descrittori di file (nella user area) -- Array di strutture, una per ogni file aperto -- Di ampiezza fissa (max 20) Il fd è l’indice del descrittore assegnato al file appena aperto 15 Apertura di un file : SC open() (3) • Tipico codice di apertura di un file : int fd; /*file descriptor */ /* tento di aprire */ fd = open(“s.c”, O_RDONLY); /* controllo errori*/ if(fd==-1) { perror(“fk, in apertura”); exit(errno); /* termina */ } 16 Apertura di un file : SC open() (4) • Tipico codice di apertura di un file – uso della macro IFERROR : int fd; /*file descriptor */ /* apertura e controllo errori usando la macro */ IFERROR(fd = open(“s.c”, O_RDONLY), “fk, in apertura”)); 17 Apertura di un file : SC open() (5) • Cosa fa la open : – segue il path del file per recuparare l’i-node corrispondente – controlla i diritti i accesso (li confronta con le richieste in flags) – se l’accesso è consentito assegna al file l’indice di una posizione libera nella tabella dei descr. (fd) • aggiorna le strutture dati interne al nucleo … – se si è verificato un errore ritorna -1 (errno) – altrimenti ritorna fd, che deve essere usato come parametro per tutti gli accessi successivi 18 Apertura di un file : SC open() (6) • Strutture di nucleo legate ai file Pos.Corrente 0 write/read fd Tabella dei descrittori di file (user area) Copia dell’i-node Tabella dei file aperti Tabella degli i-node attivi 19 Lettura: SC read() • Es: lung = read(fd,buffer,N) File descriptor -1 : errore n > 0 : numero byte letti 0 : Pos.Corrente è a fine file Numero massimo di byte da leggere (void *) puntatore all’area di memoria dove andare a scrivere i dati Effetto: Legge al più N byte a partire da Pos.Corrente, Pos.Corrente += lung 20 Lettura: SC read() (2) • Tipico ciclo di lettura da file: int fd, lung; /* fd, n byte letti */ char buf[N]; /*buffer*/ /* apertura file */ IFERROR(fd = open(“s.c”, O_RDONLY), “fk, in apertura”)); while ((lung = read(fd,buf,N))>0){ … } IFERROR(lung,”fk, in lettura”); 21 Scrittura: SC write() • Es: lung = write(fd,buffer,N) File descriptor -1 : errore n => 0 : numero byte scritti Numero massimo di byte da scrivere (void *) puntatore all’area di memoria dove andare a prendere i dati Effetto: Scrive al più N byte a partire da Pos.Corrente, Pos.Corrente += lung 22 Lettura: SC write() (2) • Es. scrittura sullo stdout (fd 1): int fd, lung; /* fd, n byte letti */ char buf[N]; /*buffer*/ IFERROR(fd = open(“s.c”, O_RDONLY), “fk, in apertura”)); while ((lung = read(fd,buf,N))>0){ IFERROR(write(1, buf, lung), “fk, in scrittura”)); } IFERROR(l,”fk, in lettura”); 23 Chiusura: la SC close() • Libera le aree di occupate nelle varie tabelle • Provoca la scrittura su file di eventuali buffer non pieni • int close (int fd) 24 Chiusura: SC close() (2) • Es. chiusura di un file …. int fd, lung; /* fd, n byte letti */ char buf[N]; /*buffer*/ IFERROR(fd = open(“s.c”, O_RDONLY), “fk, in apertura”)); while ((lung = read(fd,buf,N))>0){ IFERROR(write(1, buf, lung), “fk, in scrittura”)); } IFERROR(lung,”fk, in lettura”); IFERROR(close(fd),”fk, in chiusura”); 25 Standard input, output and error • Ogni processo Unix ha dei ‘canali di comunicazione’ predefiniti con il mondo esterno – es. $sort stdout Tipicamente lo schermo stdin Tipicamente la tastiera P stderr Tipicamente lo schermo 26 Standard input, output and error (2) • Un esempio stdin 0 stdout 1 Copia dell’i-node di ttyX stderr 2 Tabella dei descrittori di file (user area) Tabella dei file aperti Tabella degli i-node attivi 27 Su: open() vs fopen()e similari • open(), read(), write(), close() fanno parte della libreria standard POSIX per i file e corrisponde ad una SC • fopen(), fread(), fwrite(), fclose(), printf() fanno parte della libreria standard di I/O (stdio.h) definito dal comitato ANSI 28 Su: open() vs fopen()e similari (2) • le funzioni di stdio.h effettuano un I/O bufferizzato – se il programma termina in modo anomalo i buffer possono non essere svuotati in tempo • mischiare chiamate ad I/O bufferizzato e non può portare a risultati impredicibili – nel vostro programma usate o le chiamate POSIX (non bufferizzate) o le chiamate a funzioni in stdio.h (bufferizzate) ma non entrambe 29 SC che operano su file e directory Lseek, stat, opendir, closedir, readdir, rewinddir, working directory 30 Posizionamento : lseek() off_t lseek(int fd, off_t offset, int whence) – fd : file descriptor – offset : di quanti byte voglio spostarmi – whence : da dove calcolo lo spostamento. Può essere una delle seguenti macro • SEEK_SET dall’inizio, • SEEK_END dalla fine, • SEEK_CUR dalla posizione corrente – Ritorna : • la posizione corrente in caso di successo , • -1 in caso di fallimento 31 Posizionamento : lseek() (2) • Esempio …. lseek(fd, 0, SEEK _SET); lseek(fd, -1, SEEK _END); lseek(fd, 1, SEEK _CUR); • cosa ritornano queste chiamate ? 32 Attributi : stat() int stat(const char* pathfile, struct stat *buf) – pathfile : path del file – buf : puntatore alla struttura struct stat in cui verranno inserite le informazioni 33 Attributi : stat() (2) struct stat … ino_t mode_t nlink_t uid_t off_t unsgn long unsgn long time_t time_t time_t } { st_ino; /* st_mode; /* st_nlink; /* st_uid; /* st_size; /* st_blksize;/* st_blocks; /* st_atime; /* st_mtime; /* st_ctime; /* # i-nodo*/ diritti protezione*/ # hard link */ ID owner */ lung totale (byte)*/ dim blocco */ #blk 512byte occupati*/ ultimo accesso*/ ultima modifica */ ultima var dati */ 34 Attributi : stat() (3) struct stat info; IFERROR(stat(“dati”,&info), “In stat”); if if if if if (S_ISLNK(info.st_mode)){/* (S_ISREG(info.st_mode)){/* (S_ISDIR(info.st_mode)){/* (S_ISCHR(info.st_mode)){/* (S_ISBLK(info.st_mode)){/* link simbolico*/} file regolare*/} directory */} spec caratteri */} spec blocchi */} if (info.st_mode & S_IRUSR){/* r per owner */} if (info.st_mode & S_IWGRP){/* w per group */} 35 Directory • Il formato delle directory varia nei vari FS utilizzati in ambito Unix • Quando una directory viene aperta viene restituito un puntatore a un oggetto di tipo DIR (definto in dirent.h) – es. DIR* mydir; • Per leggere le informazioni sui file contenuti esiste la chiamata di sistema POSIX getdents() – non la useremo direttamente 36 Directory (2) • Useremo funzioni di libreria conformi a POSIX che lavorano sul puntatore in modo trasparente e chiamano getdents quando necessario – sez 3 manuali – es. readdir, rewinddir, opendir, closedir, getcwd – attenzione! : esiste anche una readdir ciamata di sistema 37 Directory: opendir, closedir DIR* opendir(const char* pathdir);, – pathdir: path directory – ritorna il puntatore all’handle della directory, o NULL se si è verificato un errore • quindi non si può usare direttamente la IFERROR(..), che fa il confronto con "-1”, occorre programmare la gestione degli errori esplicitamente int closedir(DIR* dir); – dir: puntatore all’ handle di una directory già aperta 38 Directory: opendir, closedir (2) DIR * d; /* esempio di apertura directory */ if ((d = opendir(".")) == NULL){ perror("nell'apertura"); exit(errno); } /* lavoro sulla directory */ /* chiusura directory */ IFERROR(closedir(d),"nella chiusura"); 39 Directory: readdir struct dirent* readdir(DIR * dir);, – dir : handle della directory – ritorna il puntatore ad una struttura struct dirent contenente le informazioni dell’elemento della directory che descrive il prossimo file – letture successive forniscono i file successivi – ritorna NULL quando i file sono finiti – per tornare all’inizio void rewinddir(DIR * dir);, 40 Directory: readdir (2) /* campi interessanti di dirent … */ struct dirent { … /* # di i-node */ long d_ino; /*lunghezza di d_name */ unsigned short d_reclen; /* nome del file */ char d_name[NAMEMAX+1]; … } 41 Directory: readdir (3) DIR * d; struct dirent * file; /* …. apertura directory */ /* lettura di tutte le entry della directory */ while ( (file = readdir(d))!=NULL) { /* ad esempio stampo gli attributi di un file */ printattr(file->d_name); } /* chiusura directory */ IFERROR(closedir(d),"nella chiusura"); } 42 Directory corrente ... int chdir(const char* path) int fchdir(int fd) • sono vere chiamate di sistema • cambiano la directory corrente con quella indicata char* getcwd(char* buf, size_t size) • permette di leggere la directory corrente dall’environment • scrive il nome in buf (per un massimo di size caratteri) • se non ci riesce ritorna NULL 43 Mappare file in memoria mmap(), mmunmap() 44 Allocazione dei processi nella RAM Process A Process B • Spazio logico dei processi A e B e memoria fisica • Condivisione dell’area testo 45 Mapping e condivisione di File Two processes can share a mapped file. Un file mappato simultaneamente in due processi 46 Mappaggio file in mamoria: mmap void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset) – fd: descrittore file da mappare – offset: inizio area fd da mappare in memoria – length: lunghezza area da mappare in memoria – start : indirizzo logico dal quale effettuare il mapping (tipicamente ignorato meglio passare NULL) – prot/flags : maschere di bit che specificano condivisione e protezione dell’area mappata 47 Mappaggio file: mmap (2) void* mmap(void* start, size_t length, int prot,int flags, int fd, off_t offset) – il valore restituito è l’indirizzo logico (iniziale) in cui il file è stato effettivamente mappato – il valore restituito è MAP_FAILED se non si riesce a mappare il file (settando errno opportunamente) 48 Mappaggio file: mmap (3) – prot : descrive la protezione dell’area mappata, si ottiene mettendo in OR un insieme di maschere predefinite. Es: • PROT_WRITE : permesso di scrittura • PROT_READ: permesso di lettura – flags : se e con quali modalità l’area di memoria può essere condivisa da più processi, si ottiene mettendo in OR un insieme di maschere predefinite. Es: • MAP_SHARED : si può condividere con tutti gli altri processi • MAP_PRIVATE: crea una copia privata del processo, le scritture non modificano il file 49 Mappaggio file: mmap (4) – ATTENZIONE: length deve essere un multiplo dell’ampiezza di pagina – l’ampiezza della pagina (in byte) si ottiene con la fne standard #include <unistd.h> int getpagesize(void); 50 Mappaggio file: mmap (5) int fd, psize, esito; char* file; /* puntatore area mappata */ psize = getpagesize(); /* ampiezza pagina */ IFERROR(fd=open(“s.c”,O_RDWR), “aprendo s.c”); /* esito è -1 se la mmap() e’ fallita */ esito =(file = mmap(NULL, psize, \ PROT_READ|PROT_WRITE, MAP_SHARED, \ fd, 0) == MAP_FAILED )?-1:0; IFERROR(esito, “mappando s.c”); /* da qua accedo al file come un array */ putchar(file[10]); 51 S-mappaggio file: munmap() int munmap(void* start, size_t length); – length: lunghezza area da s-mappare dalla memoria – start : indirizzo logico dal quale effettuare lo smapping – ritorna -1 se si è verificato un errore – NB: la chiusura di un file NON elimina i mapping relativi al file che devono essere eliminati chiamando esplicitamente la munmap() 52 Mappaggio file: esempio ... IFERROR(fd=open(“s.c”,O_RDWR), “aprendo s.c”); esito =(file = mmap(NULL, psize, \ PROT_READ|PROT_WRITE, MAP_SHARED, \ fd, 0) == MAP_FAILED )?-1:0; IFERROR(esito, “mappando s.c”); IFERROR(close(fd), “chiudendo s.c”); /* da qua accedo al file come un array */ putchar(file[10]); … IFERROR(munmap(file,psize), “smappando s.c”); 53