1 Esempio di ADT e make Operazioni bit a bit su piu’ file
2 Albero delle dipendenze complessivo dell’esempio glob.h bitbit.h bitbit.c menu_bit.h bitbit.o bit bit.o Struttura dipendenze Albero dipendenze radicato nell’eseguibile bit menu_bit.o menu_bit.c bit.c
3 Come visualizzare i moduli oggetto –Comando nm options file.o fornisce tutti i simboli definiti in file.o $nm -g bitbit.o fornisce solo i simboli esportati –Comandi objdump e readelf permettoni di leggere le varie sezioni dell’eseguibile $objdump -d bitbit.o fornisce il testo disassemblato -r tabelle di rilocazione -t symbol table –Vedere anche info binutils da emacs
4 Classi di memorizzazione
5 Definiscono le regole di visibilità delle variabili e delle funzioni quando il programma è diviso su più file Stabiliscono dove (in quale parte dello spazio di indirizzamento) allocare le varie variabili Stabiliscono cosa finisce nella tabella dei simboli del modulo oggetto –simboli (esterni, esportati)
6 register auto static extern variabili locali funzioni variabili globali Classi di memorizzazione (2) Tutti i modificatori possono essere applicati alle variabili locali Solo static ed extern a globali e funzioni
7 Variabili locali La classe di memorizzazione di default per le variabili locali è auto (automatica): int x; auto int x; –significano entrambe la stessa cosa : la variabile viene allocata sullo stack all’ingresso del blocco in cui è dichiarata e rimossa all’uscita –non c’è alcun legame fra il valore ad una certa attivazione ed il valore alla attivazione successiva –la visibilità di x è solo all’interno del blocco
8 Variabili locali (2) La classe di memorizzazione register è obsoleta int x; register int x; –significa che la variabile x è usata pesantemente e consiglia al compilatore di metterla in un registro generale –viene ignorata, i compilatori di oggi sono dotati di sofisticati algoritmi di allocazione dei registri –la visibilità di x è solo all’interno del blocco
9 Variabili locali (3) La classe di memorizzazione static è utilizzata quando il valore della variabile deve essere mantenuto da una invocazione all’altra del blocco { static int x; … } –significa che la variabile x mantiene il proprio valore da una attivazione del blocco alla successiva –la visibilità di x è solo all’interno del blocco
10 Variabili locali (4) La classe di memorizzazione extern { extern int x; … } –significa che la variabile x è dichiarata globale in questo o in altri file –il nome x finisce nella tabella dei simboli come simbolo esterno –il compilatore segnala la situazione nell’oggetto ed il linker ‘cerca x altrove’
11 Variabili globali La classe di memorizzazione extern è la condizione di default per le variabili globali int x; extern int x; int funct (…) {...} –queste due dichiarazioni di x sono equivalenti, ed x viene allocata nell’area dati statica –il nome x finisce nella tabella dei simboli come simbolo esportato –la visibilità di x comprende tutte le funzioni dichiarate nel file dopo di essa ed x può essere utilizzata in un altro file
12 Variabili globali (2) La classe di memorizzazione static serve a limitare la portata delle varibili globali static int x; int funct (…) {...} –questa dichiarazioni di x rende la variabile visibile da tutte le funzioni che la seguono nel file –MA, il nome x NON finisce nella tabella dei simboli esportati –la visibilità di x è ristretta al file dove è dichiarata –può servire per realizzare variabili ‘private’ di una implementazione
13 Funzioni extern è la classe di memorizzazione di default delle funzioni int funct (…) {...} extern int funct (…) {...} –sono equivalenti : funct è chiamabile all’interno di tutte le funzioni che la seguono nel file –il nome funct finisce nella tabella dei simboli come simbolo esportato –funzioni di altri file possono usare funct, chiamate a funzioni definite in altri file finiscono nella tabella dei simboli come esterne e vengono risolte dal linker (es. printf)
14 Funzioni (2) La classe di memorizzazione static serve a limitare la portata delle funzioni static int funct (…) {...} –questa dichiarazioni di funct la rende la visibile solo a tutte le funzioni che la seguono nel file –MA, il nome funct NON finisce nella tabella dei simboli esportati –può servire per implementare delle funzioni‘private’ di una implementazione, non chiamabili dall’esterno del file
15 ADT ‘protetti’ in C Idea di base : –definire l’interfaccia in un file X.h (tipi di dato e prototipi delle funzioni utilizzabili) –utilizzare lo specificatore static per realizzare la protezione nel file X.c che realizza l’implementazione –mettere a disposizione l’oggetto X.o (da solo o all’interno di una libreria) per permettere un linking corretto Esempio : il tipo BIT32
16 ADT ‘protetti’ in C (2)... Come si usa il tipo BIT32 –includere l’header ( bitbit.h ) –compilare linkando anche bitbit.o Insiemi di file oggetti di uso comune possono essere raccolti in librerie ….
17 Librerie (ar) Il comando : ar r nomelibreria.a file1.o fileN.o –inserisce/aggiorna i file file1.o fileN.o nella libreria nomelibreria.a –es. ar r libutils.a bitbit.o menu_bit.o crea una libreria contenente i due oggetti r : operazione da eseguire nm -s libreria.a fornisce informazioni sui simboli definiti nei vari file della libreria (con indicazioni del file dove si trovano)
18 Librerie (ar) (2) Tipicamente le librerie vengono raccolte in una directory –es. ~/lib/ –e recuperate in fase di linking per generare l’eseguibile finale –es: gcc -L~/lib/ -lutils main.o esegue il linking fra main.o e le funzioni nella libreria libutils.a in ~/lib/
19 Librerie (ar) (3) Possibile struttura di un makefile per creare ed istallare le librerie in una directory fissata :
20 SC che operano su file e directory Lseek, stat, opendir, closedir, readdir, rewinddir, ch
21 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
22 Posizionamento : lseek() (2) Esempio …. lseek(fd, 0, SEEK _SET); lseek(fd, -1, SEEK _END); lseek(fd, 1, SEEK _CUR); cosa ritornano queste chiamate ?
23 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
24 Attributi : stat() (2) struct stat { … ino_t st_ino; /* # i-nodo*/ mode_t st_mode; /* diritti protezione*/ nlink_t st_nlink; /* # hard link */ uid_t st_uid; /* ID owner */ off_t st_size; /* lung totale (byte)*/ unsgn long st_blksize;/* dim blocco */ unsgn long st_blocks; /* #blk 512byte occupati*/ time_t st_atime; /* ultimo accesso*/ time_t st_mtime; /* ultima modifica */ time_t st_ctime; /* ultima var dati */ }
25 Attributi : stat() (3) struct stat info; IFERROR(stat(“dati”,&info), “In stat”); if (S_ISLNK(info.st_mode)){/* link simbolico*/} if (S_ISREG(info.st_mode)){/* file regolare*/} if (S_ISDIR(info.st_mode)){/* directory */} if (S_ISCHR(info.st_mode)){/* spec caratteri */} if (S_ISBLK(info.st_mode)){/* spec blocchi */} if (info.st_mode & S_IRUSR){/* r per owner */} if (info.st_mode & S_IWGRP){/* w per group */}
26 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
27 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
28 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
29 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");
30 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);,
31 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]; … }
32 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"); }
33 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