fclose () / pclose () può bloccare su alcuni puntatori a file
Domanda
Calling fclose()
qui dopo dup()
ing suoi blocchi descrittore di file fino a quando il processo figlio è terminato (presumibilmente perché il flusso è terminato).
FILE *f = popen("./output", "r");
int d = dup(fileno(f));
fclose(f);
Tuttavia eseguendo manualmente il pipe()
, fork()
, execvp()
del popen()
, e quindi dup()
ing lettura descrittore di file del tubo, chiudendo l'originale non blocca.
int p[2];
pipe(p);
switch (fork()) {
case 0: {
char *argv[] = {"./output", NULL};
close(p[0]);
dup2(p[1], 1);
execvp(*argv, argv);
}
default: {
close(p[1]);
int d = dup(p[0]);
close(p[0]);
}
}
Perché si verifica, e come posso chiudere il FILE *
tornato da popen()
e utilizzare un descrittore di file al suo posto?
Aggiornamento:
Sono consapevole del fatto che la documentazione dice di usare pclose()
, tuttavia blocchi fclose()
pure. Inoltre, ho curiosato nel codice glibc, e pclose()
appena chiama fclose()
. Il comportamento è lo stesso, se viene utilizzato fclose()
o pclose()
.
Soluzione 2
Deluso con la generalità delle risposte finora (posso RTFM , tyvm), I 'Ho studiato a fondo questo, facendo un passo attraverso la lettura e il glibc fonte.
In glibc pclose()
chiamate direttamente fclose()
senza effetti aggiuntivi, così le chiamate sono 2 stesso. In realtà si potrebbe usare pclose()
e fclose()
intercambiabile. Sono sicuro che questo è puramente una coincidenza nella realizzazione evoluta, e l'uso di pclose()
per chiudere un FILE *
tornato da popen()
è ancora raccomandato.
La magia è in popen()
. FILE *
s in glibc contengono una tabella di salto con i puntatori di appropriarsi funzioni per gestire tali chiamate come fseek()
, fread()
, e di rilevanza fclose()
. Quando si chiama popen()
, un tavolo salto diverso usato da quello utilizzato dai fopen()
. L'elemento close
in questa tabella punti salto ad una funzione speciale _IO_new_proc_close
, che invita waitpid()
sul pid immagazzinata nella regione indicata da FILE *
.
Ecco lo stack di chiamate rilevante nella mia versione di glibc, che ho annotato con appunti su ciò che sta accadendo:
// linux waitpid system call interface
#0 0x00f9a422 in __kernel_vsyscall ()
#1 0x00c38513 in __waitpid_nocancel () from /lib/tls/i686/cmov/libc.so.6
// removes fp from a chain of proc files
// and waits for the process of the stored pid to terminate
#2 0x00bff248 in _IO_new_proc_close (fp=0x804b008) at iopopen.c:357
// flushes the stream and calls close in its jump table
#3 0x00c09ff3 in _IO_new_file_close_it (fp=0x804b008) at fileops.c:175
// destroys the FILEs buffers
#4 0x00bfd548 in _IO_new_fclose (fp=0x804b008) at iofclose.c:62
// calls fclose
#5 0x00c017fd in __new_pclose (fp=0x804b008) at pclose.c:43
// calls pclose
#6 0x08048788 in main () at opener.c:34
Così il corto di esso è, utilizzando popen()
, il FILE *
restituita non deve essere chiuso, anche se si dup()
suo descrittore di file, perché bloccherà fino a quando il processo figlio termina. Naturalmente, dopo questo ti verrà lasciato con un descrittore di file ad un tubo che conterrà tutto ciò che il processo figlio è riuscito a write () ad esso prima di terminare.
Per non fread()
ing con il puntatore del file restituito dal popen()
, il tubo sottostante non verrà toccato, è sicuro di utilizzare il descrittore di file da fileno()
, e finire chiamando pclose()
.
Altri suggerimenti
http://linux.die.net/man/3/popen
La funzione pclose () attende il processo associato per terminare e restituisce lo stato di uscita del comando come restituito da wait4 ().
Dal pclose () vuole restituire lo stato di uscita, deve attendere che il figlio abbia terminato e generare uno. Dal momento che fclose () chiama pclose (), lo stesso vale per fclose () per voi.
Se la forcella e exec e fare il resto da soli, non si finisce per chiamare pclose () (direttamente o indirettamente), quindi non c'è nessuna attesa a distanza di tempo. Si noti, tuttavia, che a meno che il programma è impostato per ignorare SIGCHLD, il processo non terminerà (invece che andrà zombie) fino a quando il bambino fa. Ma almeno il costo verrà eseguito per uscire prima.
Il FILE*
restituito da popen()
dovrebbe essere chiuso da pclose()
, non per fclose()
. Poi, la documentazione per pclose()
specifica:
Il pclose () funzione attende per il processo per terminare associato e restituisce lo stato di uscita del comando come restituito da wait4 ().
Quindi, l'attesa è una delle due cose pclose()
fa, oltre a chiudere il descrittore di file. close()
fa solo una cosa:. chiude il descrittore
In risposta alla tua seconda domanda, penso che si può utilizzare il descrittore restituito da fileno()
. Non c'è alcun bisogno di dup()
esso. Dopo aver finito con esso, pclose()
l'originale.