1. Regola fondamentale delle pipe
Una pipe ha:
fd[0] -> lettura
fd[1] -> scrittura2. Dopo la fork() ogni processo eredita TUTTI i file descriptor
Questa è la cosa più importante da ricordare.
Dopo:
fork();padre e figlio possiedono:
stdin
stdout
stderr
TUTTE le pipe aperteQuindi:
bisogna chiudere SEMPRE i descriptor inutili3. Regola pratica pipe
Processo che scrive
Deve chiudere:
close(fd[0]);perché non legge.
Processo che legge
Deve chiudere:
close(fd[1]);perché non scrive.
4. Dopo dup2() chiudi SEMPRE il descriptor originale
Esempio:
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);Perché dup2() crea una copia del descriptor.
Se non chiudi:
- leak di descriptor
- EOF non arriva
- deadlock possibili
5. EOF sulla pipe arriva SOLO se tutti gli writer sono chiusi
Questa è LA regola critica.
La read() termina quando:
TUTTI i lati scrittura della pipe sono chiusiSe anche UN SOLO processo mantiene aperto:
fd[1]la read() può bloccarsi per sempre.
6. Ogni figlio deve chiudere ANCHE le pipe degli altri figli
Esempio:
pipe(p1);
pipe(p2);
pipe(p3);Nel figlio associato a p1:
close(p2[0]);
close(p2[1]);
close(p3[0]);
close(p3[1]);sempre.
7. Le pipe non si riusano dopo essere state chiuse
Questa è una pipe:
pipe(fd);vive per:
creazione -> uso -> chiusuraNON:
creazione -> chiusura -> riusoSe fai cicli:
while(1)ricrea le pipe ogni iterazione.
8. Dopo fork() controlla SEMPRE il ritorno
Pattern corretto:
pid_t pid = fork();
if(pid < 0)
{
perror("fork");
exit(1);
}
if(pid == 0)
{
// figlio
}
else
{
// padre
}9. Dopo exec() il codice successivo NON dovrebbe eseguire
Se exec() funziona:
il processo viene sostituitoquindi:
execlp(...);
perror("exec");
exit(1);10. wait() / waitpid() sempre
Ogni figlio terminato senza wait():
diventa zombieQuindi:
waitpid(pid, NULL, 0);oppure:
while(wait(NULL) > 0);11. Mai fare busy waiting
MALE:
while(1);CPU al 100%.
Meglio:
pause();oppure:
sleep(1);12. Nei signal handler usare SOLO funzioni async-signal-safe
Dentro:
void handler(int sig)NON usare:
-
printf
-
malloc
-
fork
-
execlp
-
ctime
Pattern corretto:
volatile sig_atomic_t flag = 0;
void handler(int sig)
{
flag = 1;
}13. Pattern classico pipe + exec
Schema standard:
pipe(fd);
pid = fork();
if(pid == 0)
{
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
execlp(...);
exit(1);
}
else
{
close(fd[1]);
while(read(fd[0], ...) > 0)
{
...
}
close(fd[0]);
wait(NULL);
}14. Regola mentale più importante
Quando qualcosa si blocca con le pipe, chiediti sempre:
Chi sta ancora tenendo aperto il lato scrittura?Nel 90% dei casi il problema è lì.
