Scaricare la presentazione
La presentazione è in caricamento. Aspetta per favore
PubblicatoAlberta Genovese Modificato 9 anni fa
1
1 Programmazione di Sistema
2
2 Linguaggio C Larga diffusione nel software applicativo Standard di fatto per lo sviluppo di software di sistema –Visione a basso livello della memoria –Capacità di manipolare singoli bit –Possibilità di incapsulare parti di codice assembler che effettuano compiti impossibili da esprimere in linguaggi ad alto livello es. esecuzione di istruzioni ‘speciali’ (TRAP), manipolazione diretta di registri –Compilazione estremamente efficiente –Occupazione di memoria ridotta
3
3 C : caratteristiche fondamentali Paradigma imperativo –costrutti di controllo ( while-do, for, if (..) else, switch ) –tipi base ( int, double etc…) –la principale forma di strutturazione sono le funzioni un programma C è una collezione di funzioni di cui una di nome main le funzioni possono risiedere in uno o più file –in C è possibile conoscere e denotare l’indirizzo della cella di memoria in cui è memorizzata una variabile tramite i puntatori
4
4 Struttura tipica di un programma C define/include variabili globali typedef main def F1 … def FN prototipi F1..FN
5
5 Come si esegue un programma C? Prima di essere eseguito dal processore il programma deve essere –1. pre-processato –2. compilato –3. collegato (linking) –4. caricato in memoria (loading) Vediamo come funzionano le varie fasi
6
6 Pre-processing e compilazione Il programma deve essere editato e salvato in un file con estensione ‘.c’ –es. posso salvare il programma esempio nel file prova.c Non c’è alcun rapporto fra il nome del file ed il nome delle funzioni in esso contenute
7
7 Pre-processing e compilazione (2) Il compilatore può essere invocato con il comando gcc -Wall -pedantic -g prova.c -o nomees –-Wall -pedantic opzioni che aumentano il numero di controlli e di messaggi di ‘avvertimento’ visualizzati –-g opzione che include anche le informazioni necessarie al debugger –prova.c nome del file da compilare/preprocessare –-o nomees opzione che permette di decidere il nome del file eseguibile ( nomees ). Altrimenti il file eseguibile si chiama di default a.out
8
8 Pre-processing e compilazione (3) Preprocessing –#include –#include “file” queste linee vengono rimpiazzate dal contenuto dei file specificati ( viene cercato tra gli header standard, su Linux /usr/include; “file” viene cercato a partire dalla directory corrente ) –#define nome testo rimpiazza ogni occorrenza di nome con il testo specificato è possibile specificare del testo parametrico
9
9 Pre-processing e compilazione (4) Compilazione –trasforma il file preprocessato (senza più #include o #define ) in un file eseguibile che contiene alcune informazioni di carattere generale rappresentazione binaria di (parte dei) dati del programma (variabili globali) codice assembler eseguibile dal processore target (testo) –per capire come avviene questa traduzione, dobbiamo specificare come un programma vede la memoria durante la sua esecuzione (spazio di indirizzamento)
10
10 Spazio di indirizzamento Text Data Stack Area vuota Traduzione in assembler delle funzioni che compongono il programma 0 2 32 - 1 Variabili globali a tutte le funzioni Pila di FRAME, uno per ogni chiamata di funzione da cui non abbiamo ancora fatto ritorno
11
11 Spazio di indirizzamento (2) Text Data Stack Area vuota 0 2 32 - 1 Contenuti tipici di un FRAME : - variabili locali della funzione - indirizzo di ritorno (indirizzo dell’istruzione successiva a quella che ha effettuato la chiamata alla funzione) - copia dei parametri di chiamata Direzione di crescita dello stack
12
12 Spazio di indirizzamento (3) Spazio di indirizzamento Text Data Stack Area vuota 0 2 32 - 1 All’inizio dell’esecuzione lo Stack contiene solo il FRAME per la funzione main Successivamente : * ogni volta che viene chiamata una nuova funzione viene inserito un nuovo frame nello stack * ogni volta che una funzione termina ( es. return 0 ) viene eliminato il frame in cima allo stack e l’esecuzione viene continuata a partire dall’indirizzo di ritorno
13
13 Spazio di indirizzamento (4) Text Data Stack Area vuota 0 2 32 - 1 Direzione di crescita dello heap Spazio di memoria dedicato ai dati globali a tutte le funzioni. Diviso in statico per i dati la cui dimensione è nota a tempo di compilazione e dinamico (heap) heap
14
14 Compilazione : un esempio int main (void){ int i, tmp, max=0; printf(“Inserisci %d interi positivi\n”,N); for (i = 0; i < N; i++) { scanf(“%d”, &tmp); max = (max < tmp)? max : tmp ; } printf(“Il massimo è %d \n”,max); return 0; } Ogni variabile corrisponde ad una o più parole/celle di memoria &i &max &tmp 32 bit
15
15 Compilazione : un esempio (2) int main (void){ int i, tmp, max=0; printf(“Inserisci %d interi positivi\n”,N); for (i = 0; i < N; i++) { scanf(“%d”, &tmp); max = (max < tmp)? max : tmp ; } printf(“Il massimo è %d \n”,max); return 0; } Ogni statement viene tradotto in - istruzioni assembler che lavorano sulle celle rilevanti - CALL di altre funzioni (es. printf) &i &max &tmp
16
16 Formato del file eseguibile La compilazione produce un file eseguibile (es. a.out ) Il formato di un eseguibile dipende dal sistema operativo In Linux un eseguibile ha il formato ELF (Executable and Linking Format)
17
17 Formato del file eseguibile (2) –Formato di un eseguibile ELF Text segment I-Data segment Ampiezza BSS Altre info Magic number File a.out Variabili globali inizializzate Ampiezza area di memoria occupata dalle variabili globali NON inizializzate Numero che contraddistingue il file come eseguibile Codice del programma (assemblato)
18
18 Formato del file eseguibile (3) –L’eseguibile contiene tutte le informazioni per creare la configurazione iniziale dello spazio di indirizzamento (loading) Text I-Data segment Stack Area vuota 0 2 32 - 1 BSS-segment Text segment I-Data segment Ampiezza BSS Altre info Magic number File a.out Data FRAME per la funzione main
19
19 E se non me lo esegue? Se avete cercato di eseguire a.out e non ci riuscite: $$ a.out
20
20 E se non me lo esegue?(2) Se avete cercato di eseguire un file eseguibile e non ci siete riusciti: $$ a.out bash:command not found Significa che la variabile di ambiente PATH non contiene la directory corrente quindi usate il path completo $$./a.out
21
21 E se non me lo esegue?(3) Se avete cercato di eseguire un file eseguibile e non ci siete riusciti: $$ a.out bash:command not found Per settare il PATH –bash $$ export PATH=$PATH:. –csh e tcsh $$ setenv PATH=${PATH}:.
22
22 Argomenti della linea di comando Gli argomenti della linea di comando sono accessibili all’interno della funzione main() –il SO li mette sulla pila prima di attivare il processo –il formato in cui sono resi disponibili è fisso int main (int argc, char* argv [] ){ …… } Numero di argomenti nella linea di comando Array di puntatori agli argomenti (ciascuno è una stringa, tipo char*)
23
23 Argomenti della linea di comando (2) Un esempio : %> a.out una stringa per a.out 5 argc argv p er \O a.o ut s tr in g u na a a.o ut argv[0]
24
24 Argomenti della linea di comando (3) Es. schema di programma che stampa gli argomenti sulla linea di comando : int main (int argc, char* argv [] ){ …… for(i=0;i<argc;i++) printf(“arg %d: %s”,i,argv[i]); … }
25
25 Compilazione separata Scopi di suddividere il codice sorgente C su più file –per raggruppare un insieme di funzioni congruenti (e solo quelle) in un unico file –per incapsulare un insieme di funzioni esportando solo quelle che vogliamo fare utilizzare agli altri (pubbliche) –per compilare una volta per tutte un insieme di funzioni e metterlo a disposizione di altri in una libreria es. le librerie standard viste finora ….
26
26 Compilazione separata Tipicamente : define/include variabili globali typedef main def F1 … def Fk prototipi F1..Fk glob.h prototipi Fk+1..FN main.c fun_toK.c def Fk+1 … def FN fun_toK.h fun_toN.c fun_1toN.h
27
27 define/include variabili globali typedef #include “glob.h ” #include “fun_toK.h” #include “fun_toN.h” …... ass F1 … ass Fk prototipi F1..Fk glob.h prototipi Fk+1..FN main.c fun_toK.o ass Fk+1 … ass FN fun_toK.h fun_toN.o fun_1toN.h Moduli oggetto
28
28 Moduli oggetto Come si crea il modulo oggetto? –gcc -c file.c produce un file file.o che contiene il modulo oggetto di file.c –Il formato dell’oggetto dipende dal sistema operativo –Che informazioni contiene l’oggetto ? L’assemblato del sorgente testo e dati (si assume di partire dall’indirizzo 0) La tabella di rilocazione (indirizzi risolti dal compilatore) La tabella dei simboli (esportati ed esterni, risolti dal linker)
29
29 Linker Il linker si occupa di risolvere i simboli. –Analizza tutte le tabelle dei simboli. –Per ogni simbolo non risolto (esterno) cerca in tutte le altre tabelle dei simboli esportati degli oggetti da collegare (linkare) assieme nelle librerie standard nelle librerie esplicitamente collegate (opzione -l )
30
30 Chiamate di sistema Introduzione Errori : perror() Chiamate che lavorano su file
31
31 Chiamate al sistema e librerie di funzioni System call= insieme di funzioni che un programma può chiamare, per le quali viene generata un'interruzione del processo passando il controllo dal programma al kernel. Ciascuna di queste chiamate al sistema viene rimappata in opportune funzioni con lo stesso nome definite dentro la Libreria Standard del C, che, oltre alle interfacce alle system call, contiene anche le funzioni definite dai vari standard (es. ANSI C e POSIX), che sono comunemente usate nella programmazione. Programmare in Linux significa anzitutto essere in grado di usare le varie interfacce contenute nella GNU Standard C Library (glibc)
32
32 Chiamate di sistema 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
33
33 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
34
34 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
35
35 UNIX: struttura generale Utenti Programmi di utilità standard (shell, editori, compilatori etc.) Libreria standard (Open, close, read, write …) Sistema operativo Unix (gestione processi, memoria, file system, I/0..) Hardware Modo utente Interfaccia di libreria C Interfaccia delle chiamate di sistema Modo kernel
36
36 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 )
37
37 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
38
38 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)
39
39 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 $
40
40 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
41
41 Esempio Possiamo usare delle macro con parametri che inseriscono test e perror() ad ogni chiamata (le useremo in alcuni esempi successivi) /* 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;} /* uso tipico */ int main (void) { IFERROR3(read(…),”main, lettura”, return -1); IFERROR(read(…),”main, lettura”); }
42
42 SC che operano su file (1) open(), read(), write(), close()
43
43 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
44
44 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
45
45 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 */ }
46
46 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”));
47
47 Apertura di un file : SC open() (5) Cosa fa la open : –segue il path del file per recuparare i suoi attributi e gli indirizzi ai suoi blocchi dati (i-node) –controlla i diritti di 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
48
48 Tabella dei file aperti Copia dell’i-node Tabella degli i-node attivi Tabella dei descrittori di file (user area) fd Pos.Corrente 0 write/read Apertura di un file : SC open() (6) Strutture di nucleo legate ai file
49
49 Lettura: SC read() Es: lung = read(fd,buffer,N) File descriptor (void *) puntatore all’area di memoria dove andare a scrivere i dati Numero massimo di byte da leggere -1 : errore n > 0 : numero byte letti 0 : Pos.Corrente è a fine file Effetto: Legge al più N byte a partire da Pos.Corrente, Pos.Corrente += lung
50
50 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”);
51
51 Scrittura: SC write() Es: lung = write(fd,buffer,N) File descriptor (void *) puntatore all’area di memoria dove andare a prendere i dati Numero massimo di byte da scrivere -1 : errore n => 0 : numero byte scritti Effetto: Scrive al più N byte a partire da Pos.Corrente, Pos.Corrente += lung
52
52 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”);
53
53 Chiusura: la SC close() Libera le aree di occupate nelle varie tabelle int close (int fd)
54
54 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”);
55
55 Standard input, output and error Ogni processo Unix ha dei ‘canali di comunicazione’ predefiniti con il mondo esterno –es. $sort P stdin stdout stderr Tipicamente la tastiera Tipicamente lo schermo
56
56 Tabella dei descrittori di file (user area) 0 Standard input, output and error (2) Un esempio stdin stdout stderr 1 2 Tabella dei file aperti Copia dell’i-node di ttyX Tabella degli i-node attivi
57
57 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
58
58 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
59
59 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
60
60 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
61
61 Attributi : stat() (2) struct stat { … ino_t st_ino; /* numero dell’i-node*/ 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 */ }
62
62 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 */}
63
63 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;
64
64 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 int closedir(DIR* dir); –dir: puntatore all’ handle di una directory già aperta
65
65 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");
66
66 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);,
67
67 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]; … }
68
68 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"); }
69
69 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 scrive il nome in buf (per un massimo di size caratteri) se non ci riesce ritorna NULL
Presentazioni simili
© 2024 SlidePlayer.it Inc.
All rights reserved.