2012-07-22 33 views
5

Dado el siguiente código:¿Qué sucede si un proceso secundario no cierra la tubería de escritura, mientras lee?

int main(int argc, char *argv[]) 
{ 
    int pipefd[2]; 
    pid_t cpid; 
    char buf; 

    if (argc != 2) { 
     fprintf(stderr, "Usage: %s \n", argv[0]); 
     exit(EXIT_FAILURE); 
    } 

    if (pipe(pipefd) == -1) { 
     perror("pipe"); 
     exit(EXIT_FAILURE); 
    } 

    cpid = fork(); 
    if (cpid == -1) { 
     perror("fork"); 
     exit(EXIT_FAILURE); 
    } 

    if (cpid == 0) { /* Child reads from pipe */ 
     close(pipefd[1]);   /* Close unused write end */ 

     while (read(pipefd[0], &buf, 1) > 0) 
      write(STDOUT_FILENO, &buf, 1); 

     write(STDOUT_FILENO, "\n", 1); 
     close(pipefd[0]); 
     _exit(EXIT_SUCCESS); 

    } else {   /* Parent writes argv[1] to pipe */ 
     close(pipefd[0]);   /* Close unused read end */ 
     write(pipefd[1], argv[1], strlen(argv[1])); 
     close(pipefd[1]);   /* Reader will see EOF */ 
     wait(NULL);    /* Wait for child */ 
     exit(EXIT_SUCCESS); 
    } 
return 0; 

} 

Cada vez que el proceso hijo quiere leer de la tubería, se debe cerrar primero el lado del tubo de la escritura. Cuando elimino esa línea close(pipefd[1]); del proceso secundario if, , básicamente digo que "está bien, el niño puede leer desde la tubería, pero estoy permitiendo que el padre escriba en la tubería al mismo tiempo".

En caso afirmativo, ¿qué sucedería si la tubería está abierta para leer &? Sin exclusión mutua?

+0

Se garantiza que la lectura() y la escritura() en una tubería son atómicas. (hasta el tamaño PIPE_BUFF). Eso significa: primero llegado, primero servido. Las partes escritas/leídas estarán entrelazadas, pero sus límites aún están intactos. – wildplasser

+2

Si no cierra el extremo de escritura del niño del conducto, el niño nunca verá un EOF, ya que EOF solo aparecerá cuando el final de escritura haya sido cerrado por todos sus usuarios. Ver http://stackoverflow.com/questions/7868018/last-child-forked-will-not-die para ver un ejemplo. – ninjalj

Respuesta

13

Cada vez que el proceso hijo quiere leer de la tubería, se debe cerrar primero el lado del tubo de la escritura.

Si el proceso - principal o secundario - no va a utilizar el extremo de escritura de una tubería, debe cerrar ese descriptor de archivo. Del mismo modo para el final de lectura de una tubería. El sistema supondrá que podría producirse una escritura mientras que cualquier proceso tiene el final de escritura abierto, incluso si el único proceso de este tipo es el que actualmente está tratando de leer desde la tubería, y el sistema no informará EOF, por lo tanto. Además, si llena una tubería en exceso y todavía hay un proceso con el final de lectura abierto (incluso si ese proceso es el que intenta escribir), la escritura se bloqueará, esperando que el lector deje espacio para que se complete la escritura.

Cuando elimino esa línea cerca (pipefd [1]); del proceso del niño SI, básicamente digo que "está bien, el niño puede leer de la tubería, pero estoy permitiendo que el padre escriba en la tubería al mismo tiempo".

No; usted está diciendo que el niño puede escribir en la tubería, así como en el padre. Cualquier proceso con el descriptor de archivo de escritura para la tubería puede escribir en la tubería.

En caso afirmativo, ¿qué sucedería si la tubería está abierta tanto para leer como para escribir, sin exclusión mutua?

No existe exclusión mutua alguna vez. Cualquier proceso con el descriptor de escritura de tubería abierto puede escribir en la tubería en cualquier momento; el kernel asegura que dos operaciones de escritura simultáneas de hecho están serializadas. Cualquier proceso con el descriptor de lectura de tubería abierto puede leerse desde la tubería en cualquier momento; el kernel asegura que dos operaciones de lectura concurrentes obtengan diferentes bytes de datos.

Asegúrate de que se utiliza una tubería de forma unidireccional, asegurándote de que solo un proceso esté abierto para escritura y solo un proceso esté abierto para su lectura. Sin embargo, esa es una decisión de programación. Podría tener N procesos con el final de escritura abierto y M procesos con el final de lectura abierto (y, perecer el pensamiento, podría haber procesos en común entre el conjunto de N y el conjunto de M procesos), y todos podrían para trabajar sorprendentemente sano. Pero no podrá prontamente predecir dónde se leerá un paquete de datos después de haber sido escrito.

+1

¡Gran respuesta! +100 si pudiera! – ron

3

fork() duplica los identificadores de archivo, por lo que tendrá dos controles para cada extremo del conducto.

Ahora, considere esto. Si el elemento primario no cierra el extremo no utilizado de la tubería, seguirá habiendo dos controles para ella. Si el niño muere, el mango del lado del niño desaparece, pero aún queda la manija abierta en poder del padre; por lo tanto, nunca habrá una "pipa rota" o "EOF" llegando porque la pipa aún es perfectamente válida. Simplemente, ya nadie está poniendo datos en él.

Lo mismo para la otra dirección, por supuesto.

Sí, el padre/hijo aún podría usar el controlador para escribir en su propia tubería; Sin embargo, no recuerdo un caso de uso para esto, y aún le da problemas de sincronización.

1

Cuando se crea la tubería, tiene dos extremos, el extremo de lectura y el de escritura. Estas son entradas en la tabla de descriptores del archivo de usuario.

De forma similar, habrá dos entradas en la tabla Archivo con 1 como recuento de referencia tanto para el final de lectura como para el final de escritura.

Ahora cuando tenedor, un niño que es creado es el archivo de descriptores se duplican y por lo tanto el número de referencias de los dos extremos de la tabla de archivos se convierte 2.

Ahora "Cuando quito esa línea estrecha (pipefd [1]) "-> En este caso, incluso si el padre ha completado la escritura, su ciclo while debajo de esta línea bloqueará para siempre que la lectura devuelva 0 (es decir, EOF). Esto sucede porque incluso si el padre ha completado la escritura y cerrado el final de escritura del conducto, el recuento de referencia del final de escritura en la tabla Archivo sigue siendo 1 (Inicialmente era 2) y la función de lectura todavía está esperando algunos datos para llegar lo que nunca sucederá

Ahora si no ha escrito "close (pipefd [0]);" en el padre, este código actual puede no mostrar ningún problema, ya que está escribiendo una vez en el padre.

Pero si escribe más de una vez, lo ideal sería obtener un error (si el niño ya no está leyendo), pero como el final de lectura en el elemento primario no está cerrado, no obtendrá el error (Incluso si el niño no está más allí para leer).

Por lo tanto, el problema de no cerrar los extremos no utilizados se hace evidente cuando estamos continuamente leyendo/escribiendo datos. Esto puede no ser evidente si solo estamos leyendo/escribiendo datos una vez.

Como si en lugar del bucle de lectura en el niño, está utilizando solo una vez la línea siguiente, donde obtiene todos los datos de una vez, y sin preocuparse por EOF, su programa funcionará incluso si no están escribiendo "close (pipefd [1]);" en el niño

read(pipefd[0], buf, sizeof(buf));//buf is a character array sufficiently large 
+0

Tenga cuidado con la distinción entre [abrir descripciones de archivos] (http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_253) y [abrir descriptores de archivos] (http: //pubs.opengroup. org/onlinepubs/9699919799/basedefs/V1_chap03.html # tag_03_166). Es realmente difícil ser preciso. El conteo está en la descripción del archivo abierto, no en el descriptor. –

+0

@JonathanLeffler Gracias Jonathan.Las "entradas de tabla de archivos" que he mencionado en la respuesta y "descripciones de archivos abiertos" esperan que se refieran a lo mismo. –

1

página del manual de tubería() para SunOS: - Leer pide a un tubo de vacío (no hay datos almacenados temporalmente) con una sola final (todos los descriptores de archivo de escritura cerrados) devolver un EOF (final de archivo).

A SIGPIPE signal is generated if a write on a pipe with only 
one end is attempted. 
Cuestiones relacionadas