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
Scarica

PPT