Pregunta

Llamando fclose() aquí después dup()ing sus bloques de descriptores de archivos hasta que el proceso hijo ha terminado (presumiblemente debido a la corriente ha terminado).

FILE *f = popen("./output", "r");
int d = dup(fileno(f));
fclose(f);

Sin embargo al realizar manualmente la pipe(), fork(), execvp() del popen(), y luego dup()ing descriptor de archivo leído de la tubería, cerrando el original no bloquea.

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]);
    }
}

¿Por qué ocurre esto, y cómo puedo cerrar la FILE * regresó de popen() y utilizar un descriptor de archivo en su lugar?

Actualización:

Soy consciente de que la documentación dice que use pclose(), sin embargo bloques fclose() también. Por otra parte, me hurgó en el código glibc, y pclose() sólo llama fclose(). El comportamiento es el mismo, si se usa fclose() o pclose().

¿Fue útil?

Solución 2

decepcionado con la generalidad de las respuestas hasta ahora (lo que pueda RTFM , tyvm), I 'he investigado esto a fondo, dando un paso a través de la lectura y la href="http://www.gnu.org/software/libc/" rel="noreferrer"> glibc fuente

En pclose() glibc llamadas directamente fclose() con ningún efecto adicional, por lo que las 2 llamadas son los mismos. En realidad, se podría utilizar pclose() y fclose() indistintamente. Estoy seguro que esto es puramente una coincidencia en la ejecución evolucionada, y el uso de pclose() para cerrar un FILE * regresó de popen() todavía se recomienda.

La magia está en popen(). FILE *s en glibc contienen una tabla de saltos con punteros a funciones adecuadas para manejar las llamadas tales como fseek(), fread(), y de relevancia fclose(). Al llamar popen(), una mesa de salto diferente utiliza a la utilizada por fopen(). El miembro close en esta tabla de puntos salto a un _IO_new_proc_close función especial, que llama waitpid() en el PID almacenado en la región a la que apunta FILE *.

Aquí está la pila de llamadas relevante en mi versión de glibc, que he anotado con notas acerca de lo que está pasando:

// 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

Así que el corto de él es, usando popen(), la FILE * devuelto no debe estar cerrada, incluso si dup() su descriptor de archivo, ya que bloqueará hasta que el proceso hijo termina. Por supuesto, después de esto se le dejó con un descriptor de archivo a una tubería que contendrá lo que el proceso hijo logró escribir () para que antes de terminar.

Al no fread()ing con el puntero del archivo de regresar de popen(), no se tocó el tubo subyacente, que es seguro utilizar el descriptor de archivo por fileno(), y terminar llamando pclose().

Otros consejos

http://linux.die.net/man/3/popen

La función pclose () espera a que el proceso asociado para terminar y devuelve el estado de salida del comando como devuelto por wait4 ().

Desde pclose () quiere devolver el estado de salida, tiene que esperar a que el niño es terminar y generar una. Desde fclose () llama pclose (), lo mismo se aplica a fclose () para usted.

Si tenedor y exec y el resto a sí mismo, que no terminan llamando pclose () (directa o indirectamente), por lo que no es necesario esperar al tiempo de cierre. Tenga en cuenta sin embargo, que a menos que su programa está configurado para ignorar SIGCHLD, el proceso no terminará (en lugar que va a ir zombi) hasta que el niño hace. Pero al menos su costo correrá a salir en primer lugar.

El FILE* devuelto por popen() debe ser cerrado por pclose(), no por fclose(). A continuación, la documentación para pclose() especifica:

  

El pclose () la función de espera a que el   terminación del proceso asociado y   devuelve el estado de salida del comando   devuelto por wait4 ().

Así que la espera es una de las dos cosas pclose() hace, además de cerrar el descriptor de archivo. close() hace sólo una cosa:. cierra el descriptor

En respuesta a la segunda pregunta, creo que se puede utilizar el descriptor devuelto por fileno(). No hay necesidad de que dup(). Después de que haya terminado con él, pclose() el original.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top