System Call per Comunicazione
tra Processi
Pipe
1
Pipe
• Pipe : file speciali utilizzati per connettere due processi con un
canale di comunicazione
B
A
pipe
• Se B cerca di leggere da una pipe vuota si blocca
• Quando la pipe è piena A viene automaticamente sospeso
• L’ampiezza della pipe dipende dal sistema (in Lunux 4K)
2
Pipe con nome e senza nome
–
–
–
pipe senza nome (unnamed)
file senza un nome visibile, che viene utilizzato per
comunicazioni fra processi che condividono puntatori alla
tabella dei file aperti (es. padre figlio, nonno nipote etc)
pipe con nome (named)
file speciale (p) con nome visibile a tutti gli altri processi
sulla stessa macchina
in entrambi i casi lettura/scrittura dalla pipe viene effettuata
con le system call read()e write() utilizzate anche per
gli altri file
3
Creazione di pipe senza nome: pipe()
int pipe(int filedes[2]);
–
–
crea un file speciale di tipo pipe
restituisce due file descriptor:
• filedes[1] per la scrittura sul pipe e
• filedes[0] per la lettura dal pipe
– restituisce 0 in caso di successo
– restituisce -1 in caso di fallimento
•
es. il processo sta usando troppi file descriptor ...
4
Creazione di pipe senza nome (2)
/* frammento che crea un nuovo pipe */
int fd[2];
/* per i descrittori */
…
IFERROR( pipe(fd),”creazione pipe”);
/*
fd[1] puo’ essere usato per la scrittura
fd[0] per la lettura
*/
5
Uso di una pipe senza nome
• Processo scrittore :
– usa le write()
– può scrivere solo se il file descriptor per la lettura
(fd[0]) non è stato ancora chiuso
• altrimenti riceve un segnale SIGPIPE che per default
termina il processo
– la scrittura è atomica
6
Uso di una pipe senza nome (2)
•
Processo lettore :
–
–
–
–
usa la read()
se il file descriptor per la scrittura (fd[1]) è stato chiuso la
read restituisce 0 (che indica la fine dell’input dalla pipe)
se la pipe è vuota ma fd[1] non è stato ancora chiuso la
read si blocca in attesa di dati sul pipe
se si cerca di leggere più byte di quelli presenti nel pipe la
read ha successo ma il numero di byte letti restituito è
minore del valore del terzo parametro
7
Uso di una pipe senza nome (3)
•
•
Nota: non serve usare lseek per il posizionamento
in un file di tipo pipe
Pipe senza nome solo per processi discendenti
–
•
•
si passano i file descriptor attraverso la condivisione dei
puntatori alla tabella dei file aperti
Si usa per comunicazione unidirezionale
Non esiste il concetto di messaggio
–
–
byte non strutturati
i processi comunicanti devono stabilire un protocollo
esplicito per scambiarsi messaggi di una certa lunghezza o
messaggi di lunghezza variabile
8
Uso di una pipe senza nome (4)
•
Sequenza tipica di utilizzo di una pipe senza nome:
– il padre crea il canale di comunicazione (pipe)
– il padre si duplica con una fork()
•
i file descriptor del padre sono copiati nella file descriptor table del
figlio
– il processo scrittore (padre o figlio) chiude fd[0] mentre il
processo lettore chiude fd[1]
– i processo comunicano con read/write
– quando la comunicazione è finita ognuno chiude la propria
estremità del canale di comunicazione
9
Uso di una pipe senza nome (5)
int fd[2];
char msg[100];
/* per i descrittori */
/* msg di lunghezza 100*/
IFERROR( pipe(fd),”creazione pipe”);
IFERROR( pid=fork(),”creazione figlio”);
if ( pid ) { /* siamo nel padre=lettore */
close(fd[1]); /* chiude scrittura */
read(fd[0],msg,100);
WRITELN(msg); /* scrive il msg letto*/
} else { /* siamo nel figlio=scrittore */
close(fd[0]); /* chiude lettura */
sprintf(msg,”Da %d: Hi!!\n”,getpid());
write(fd[1],msg,100);
close(fd[1]);}
10
Messaggi di lunghezza variabile
• Possibile protocollo:
– ogni messaggio logico è implementato da due
messaggi fisici sul canale
– il primo messaggio (di lunghezza nota),
specifica la lunghezza lung
– il secondo messaggio (di lunghezza lung)
codifica il messaggio vero e proprio
• vediamo un frammento di possibile
implementazione
11
Messaggi di lunghezza variabile (2)
…
} else { /* siamo nel figlio */
int lung;
/* per la lunghezza*/
close(fd[0]); /* chiude lettura */
sprintf(msg,”Da %d: Hi!!\n”,getpid());
lung = strlen(msg) + 1; /* terminatore */
/* primo messaggio*/
write(fd[1], &lung,sizeof(int));
/* secondo messaggio */
write(fd[1],msg,lung);
close(fd[1]);
}
12
Messaggi di lunghezza variabile (3)
…
if ( pid ) { /* siamo nel padre */
int l,lung;
/* per la lunghezza */
char* msg;
/* per il messaggio*/
close(fd[1]);
/* chiude scrittura */
IFERROR(l=read(fd[0],&lung,sizeof(int)),
”lettura”);
msg = malloc(lung);
IFERROR(l=read(fd[0],&msg,lung),
”lettura”);
WRITELN(msg); /* scrive il msg letto*/
free(msg);
/* se non serve piu’ */
close(fd[0]); }
13
Creazione di pipe con nome: mkfifo
int mkfifo(const char * pathname,
mode_t mode);
–
crea un file speciale di tipo pipe con il nome e la posizione
specificati in pathname ed i diritti di accesso specificati
da mode
– restituisce 0 se ha avuto successo e -1 altrimenti
•
–
se il file esiste già da errore
attenzione ai diritti! I diritti del file creato non sono
direttamente 0644 ma vengono modificati a seconda del
valore di una variabile della shell (umask)
•
vale ogni volta che creiamo un file (es. open …)
14
Shell : umask
• es. se create un file con
open(“ff”,O_CREAT|O_RDWR,0666)
probabilmente i diritti del file ff dopo l’esecuzione
deall SC saranno
r w - r - - r - e non, come specificato dal terzo parametro
r w - r w - r w 1 1 0 1 1 0 1 1 0
6
6
6
?????????
15
Shell : umask (2)
• umask è una maschera che fornisce una
restrizione ai diritti di accesso (modo) di un file
al momento della creazione
• il modo del file viene calcolato come
(modo&~(umask))
• Tipicamente umask = 022 quindi :
1
0
1
1
r
1
0
1
1
w
0
0
1
0
-
1
0
1
1
r
1
1
0
0
-
0
0
1
0
-
1
0
1
1
r
1
1
0
0
-
0
0
1
0
-
(modo 0666)
(umask)
(~umask)
(modo & (~umask))
16
Shell : umask (3)
• Si può modificare il valore di umask con il
comando umask
– $umask
fornisce il valore corrente della maschera
– $umask valore_ottale
setta umask al valore_ottale specificato
17
Creazione di pipe con nome: mkfifo (2)
Es:
mkfifo(“pippo”, 0664);
– se i diritti del file non sono quelli desiderati:
> ls -l pippo
prw-r--r-- 1 susanna ……
pippo
possiamo cambiarli con chmod() es
chmod(“pippo”, 0664);
adesso …
> ls -l pippo
prw-rw-r-- 1 susanna ……
pippo
18
Creazione di pipe con nome (3)
/* frammento che crea un nuovo pipe con nome */
…
IFERROR( mkfifo(“ff”,0664),”creazione pipe”);
IFERROR( chmod(“ff”,0664),”diritti pipe”);
19
Uso di una pipe con nome
• Prima di iniziare a scrivere/leggere la pipe deve
essere aperta contemporaneamente da un
lettore e da uno scrittore:
• Apertura da parte di un processo scrittore :
– usa le open()
– se nessun lettore ha ancora invocato la open() si
blocca in attesa del primo lettore
– usando le opzioni O_NONBLOCK, O_NDELAY se
non ci sono lettori la open termina subito con
fallimento
20
Uso di una pipe con nome (2)
• Apertura da parte di un processo lettore :
– usa le open()
– se nessun scrittore ha ancora invocato la open() si
blocca in attesa dello scrittore
– usando le opzioni O_NONBLOCK, O_NDELAY se
non ci sono scrittori la open termina subito con
successo
21
Uso di una pipe con nome (3)
• Tipico uso : più client e un server
– il server crea la pipe e la apre in lettura
– i client aprono in scrittura la stessa pipe
• La capienza è 40K
22
Rimozione di una pipe: unlink()
int unlink(const char*patname);
– decrementa il conto degli hard link del file indicato da pathname
– se il numero degli hard link si è azzerato e nessun processo ha il file
aperto si elimina il file dal file system
– se un processo ha ancora il file aperto,
•
•
–
un file regolare continua ad esistere finche l’ultimo descrittore è chiuso
un pipe con nome può essere usato solo da chi lo ha già aperto (viene
rimosso il nome)
ritorna 0 (successo) o -1 (fallimanto)
23
Scarica

PPT