programa program metodo example c unix fork file-descriptor

program - ¿Alguien puede explicar una descripción simple con respecto al ''descriptor de archivo'' después de fork()?



programa en c fork (2)

En "Programación avanzada en el entorno Unix", segunda edición, por W. Richard Stevens.

Sección 8.3 Función de horquilla.

Aquí está la descripción:

Es importante que el padre y el hijo compartan el mismo desplazamiento de archivo.

Considere un proceso que bifurca a un niño, luego espera a que el niño complete. Suponga que ambos procesos escriben en salida estándar como parte de su procesamiento normal. Si el padre tiene su salida estándar redirigida (por un intérprete de comandos, tal vez), es esencial que el niño actualice la compensación del archivo del padre cuando el niño escribe en la salida estándar.

[1. Qué significa eso? si la salida estándar de los padres se redirecciona a un ''archivo1'', por ejemplo, ¿qué debe actualizar el hijo después de que el niño escriba? el desplazamiento de salida estándar original de los padres o el desplazamiento de salida redirigido (es decir, archivo1)? No puede ser el último, ¿verdad?]

[2. ¿Cómo se realiza la actualización? por niño explícitamente, por SO implícitamente, por el descriptor de archivos en sí? Después de la bifurcación, pensé que padre e hijo siguieron sus propios caminos y tienen su propia COPIA del descriptor de archivo. Entonces, ¿cómo se actualiza la actualización infantil al lado de los padres?]

En este caso, el niño puede escribir en la salida estándar mientras el padre lo está esperando; al finalizar el niño, el padre puede continuar escribiendo en la salida estándar, sabiendo que su salida se agregará a lo que sea que el niño haya escrito. Si el padre y el hijo no compartían el mismo desfase de archivos, este tipo de interacción sería más difícil de lograr y requeriría acciones explícitas del padre.

Si el padre y el hijo escriben en el mismo descriptor, sin ninguna forma de sincronización, como que el padre espere al hijo, su salida se entremezclará (suponiendo que sea un descriptor que estaba abierto antes de la bifurcación). Aunque esto es posible, no es el modo normal de operación.

Hay dos casos normales para manejar los descriptores después de un tenedor.

  1. El padre espera que el niño complete. En este caso, el padre no necesita hacer nada con sus descriptores. Cuando el niño finaliza, cualquiera de los descriptores compartidos que el niño leyó o escribió tendrá sus correcciones de archivos actualizadas en consecuencia.

  2. Tanto el padre como el hijo siguen sus propios caminos. Aquí, después del tenedor, el padre cierra los descriptores que no necesita, y el niño hace lo mismo. De esta forma, ninguno interfiere con las descripciones abiertas del otro. Este escenario es a menudo el caso con los servidores de red ".

[3. Cuando se invoca fork (), todo lo que entiendo es que el niño obtiene una COPIA de lo que tiene el padre, el descriptor de archivo en este caso y lo hace. Si cualquier desplazamiento cambia al descriptor de archivo que comparten el padre y el hijo, solo puede ser porque el descriptor recuerda el desplazamiento en sí. ¿Estoy en lo cierto?]

Lo siento, soy un poco nuevo en los conceptos.

¿Alguna ayuda? Gracias.


En la misma sección, del libro, hay un diagrama que muestra tres tablas que están allí cuando se abre un archivo.

La tabla del descriptor de archivo de usuario (parte de la entrada de la tabla de proceso), la tabla de archivos de archivos y la tabla de inodos (tabla v-node). Ahora una entrada filedescriptor (que es un índice para la tabla de descriptores de archivos) apunta a una entrada de tabla de archivos, que apunta a una entrada de tabla de inodo.
Ahora el desplazamiento del archivo (la posición desde donde ocurre la próxima lectura / escritura) está en la tabla Archivo.

Así que supongamos que tiene un archivo abierto en Parent, lo que significa que tiene un descriptor, una entrada de tabla de archivos y una referencia de inodo también.
Ahora cuando está creando un niño, la tabla de descriptores de archivos se copia para el niño. Por lo tanto, el recuento de referencias en la entrada de la tabla de archivos (para ese descriptor abierto) aumenta, lo que significa que ahora hay dos referencias para la misma entrada en la tabla de archivos.

Este descriptor ahora está disponible tanto en padres como en hijos, apuntando a la misma entrada en la Tabla de archivos, por lo tanto, compartiendo el desplazamiento. Ahora teniendo este trasfondo, permítanos ver sus preguntas,

  1. Qué significa eso? si la salida estándar de los padres se redirecciona a un ''archivo1'', por ejemplo, ¿qué debe actualizar el hijo después de que el niño escriba? el desplazamiento de salida estándar original de los padres o el desplazamiento de salida redirigido (es decir, archivo1)? No puede ser el último, ¿verdad?]

El niño explícitamente no necesita actualizar nada. El autor del libro está tratando de
Dígalo, suponga que la salida estándar de los padres se redirecciona a un archivo y se realiza una llamada. Después de eso, el padre está esperando. Por lo tanto, el descriptor ahora está duplicado, es decir, el desplazamiento del archivo también se comparte. Ahora, cada vez que el niño escribe algo para la salida estándar, los datos escritos se guardan en el archivo redirigido. El desplazamiento se incrementa automáticamente mediante la llamada de escritura.

Ahora diga que el niño sale. Entonces, los padres dejan de esperar y escriben algo sobre la salida estándar (que se redirige). Ahora donde se colocará la salida de la llamada de escritura del padre -> después de los datos, que fue escrita por el niño. Por qué -> ya que el valor actual de la compensación ahora se cambia después de que el niño haya escrito.

Parent ( ) { open a file for writing, that is get the descriptor( say fd); close(1);//Closing stdout dup(fd); //Now writing to stdout means writing to the file close(fd) //Create a child that is do a fork call. ret = fork(); if ( 0 == ret ) { write(1, "Child", strlen("Child"); exit .. } wait(); //Parent waits till child exit. write(1, "Parent", strlen("Parent"); exit .. }

Pl. vea el pseudo código anterior, los datos finales que contiene el archivo abierto serán ChildParent. Por lo tanto, puede ver que la compensación del archivo cambió cuando el niño escribió y esto estuvo disponible para la llamada de escritura del padre, ya que el más reciente se compartió.

2. ¿Cómo se realiza la actualización? por niño explícitamente, por SO implícitamente, por el descriptor de archivos en sí? Después de la bifurcación, pensé que padre e hijo siguieron sus propios caminos y tienen su propia COPIA del descriptor de archivo. Entonces, ¿cómo se actualiza la actualización infantil al lado de los padres?]

Now I think the answer is clear-> by the system call that is by the OS.

[3. Cuando se invoca fork (), todo lo que entiendo es que el niño obtiene una COPIA de lo que tiene el padre, el descriptor de archivo en este caso y lo hace. Si cualquier desplazamiento cambia al descriptor de archivo que comparten el padre y el hijo, solo puede ser porque el descriptor recuerda el desplazamiento en sí. ¿Estoy en lo cierto?]

Esto también debería ser claro. La entrada de la tabla de archivos de usuario apunta a la entrada de la tabla de la tabla de archivos (que contiene el desplazamiento).

En otras palabras, las llamadas al sistema pueden obtener el desplazamiento del descriptor.


Es importante distinguir entre el descriptor de archivo , que es un pequeño entero que el proceso usa en sus llamadas de lectura y escritura para identificar el archivo, y la descripción del archivo , que es una estructura en el kernel. El desplazamiento del archivo es parte de la descripción del archivo. Vive en el kernel.

Como ejemplo, usemos este programa:

#include <unistd.h> #include <fcntl.h> #include <sys/wait.h> int main(void) { int fd; fd = open("output", O_CREAT|O_TRUNC|O_WRONLY, 0666); if(!fork()) { /* child */ write(fd, "hello ", 6); _exit(0); } else { /* parent */ int status; wait(&status); write(fd, "world/n", 6); } }

(Se ha omitido toda comprobación de errores)

Si compilamos este programa, llámalo hello y ejecútelo así:

./hello

esto es lo que sucede:

El programa abre el archivo de output , lo crea si ya no existe o lo trunca a tamaño cero si existiera. El kernel crea una descripción de archivo (en el kernel de Linux es un struct file ) y lo asocia con un descriptor de archivo para el proceso de llamada (el entero no negativo más bajo que no está en uso en la tabla de descriptores de archivo de ese proceso). El descriptor de archivo se devuelve y se asigna a fd en el programa. En aras del argumento, supongamos que fd es 3.

El programa hace un tenedor (). El nuevo proceso secundario obtiene una copia de la tabla de descriptores de archivos de sus padres, pero la descripción del archivo no se copia. La entrada número 3 en las tablas de archivos de ambos procesos apunta al mismo struct file .

El proceso principal espera mientras el proceso secundario escribe. La escritura del niño hace que la primera mitad de "hello world/n" se almacene en el archivo y adelanta el desplazamiento del archivo en 6. ¡El desplazamiento del archivo está en el struct file !

El hijo sale, la wait() del padre wait() finaliza y el padre escribe, usando fd 3 que todavía está asociado con la misma descripción del archivo cuyo archivo compensado fue actualizado por la write() del niño write() . Por lo tanto, la segunda mitad del mensaje se almacena después de la primera parte, sin sobreescribirlo como lo hubiera hecho si el padre tuviera un desfase de archivo de cero, que sería el caso si la descripción del archivo no se compartiera.

Finalmente el padre sale, y el kernel ve que el struct file ya no está en uso y lo libera.