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().

È stato utile?

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top