Trabajo de fork () en linux gcc

fork() crea un nuevo proceso y el proceso secundario comienza a ejecutarse desde el estado actual del proceso principal.

Esto es lo que sé sobre fork() en Linux.

Entonces, de acuerdo con el siguiente código:

 int main() { printf("Hi"); fork(); return 0; } 

necesita imprimir “Hola” una sola vez según lo anterior.

Pero al ejecutar lo anterior en Linux, comstackdo con gcc, se imprime “Hola” dos veces .

¿Puede alguien explicarme qué está pasando realmente con el uso de la fork() y si he entendido correctamente el funcionamiento de la fork() ?

(Incorporación de alguna explicación de un comentario del usuario @Jack) Cuando imprime algo en la salida estándar “Salida estándar” (monitor de la computadora por lo general, aunque puede redirigirlo a un archivo), se almacena inicialmente en el búfer temporal.

Ambos lados de la horquilla heredan el búfer no enjuagado, por lo que cuando cada lado de la horquilla toca la statement de devolución y finaliza, se enjuaga dos veces.

Antes de bifurcar, debe fflush(stdout); que vaciará el buffer para que el niño no lo herede.

stdout a la pantalla (a diferencia de cuando lo está redirigiendo a un archivo) está en realidad amortiguado por los extremos de línea, por lo que si había hecho printf("Hi\n"); no hubieras tenido este problema porque habría vaciado el buffer en sí mismo.

printf("Hi"); en realidad, no imprime inmediatamente la palabra “Hola” en su pantalla. Lo que hace es llenar el búfer de stdout con la palabra “Hola”, que luego se mostrará una vez que el búfer esté “sonrojado”. En este caso, stdout apunta a su monitor (supuestamente). En ese caso, el búfer se vaciará cuando esté lleno, cuando lo fuerce a enjuagar o (más comúnmente) cuando imprima un carácter de nueva línea (“\ n”). Como el búfer aún está lleno cuando se llama fork() , tanto el proceso principal como el secundario lo heredan y, por lo tanto, ambos imprimirán “Hola” cuando limpien el búfer. Si llamas a fflush(stout); antes de llamar al tenedor debería funcionar:

 int main() { printf("Hi"); fflush(stdout); fork(); return 0; } 

Alternativamente, como dije, si incluye una nueva línea en su printf debería funcionar también:

 int main() { printf("Hi\n"); fork(); return 0; } 

En general, es muy inseguro tener manejadores / objetos abiertos en uso por las bibliotecas a cada lado de fork ().

Esto incluye la biblioteca estándar de C.

fork () hace dos procesos de uno, y ninguna biblioteca puede detectarlo. Por lo tanto, si ambos procesos siguen ejecutándose con los mismos descriptores de archivo / sockets, etc., ahora tienen estados diferentes pero comparten los mismos identificadores de archivo (técnicamente tienen copias, pero los mismos archivos subyacentes). Esto hace que las cosas malas sucedan.

Ejemplos de casos en los que fork () causa este problema

  • stdio eg tty entrada / salida, tubos, archivos de disco
  • Zócalos utilizados, por ejemplo, por una biblioteca de cliente de base de datos
  • Los zócalos en uso por un proceso de servidor, que pueden tener efectos extraños cuando un niño da servicio a un zócalo hereda un identificador de archivo para otro, obtener este tipo de progtwigción correctamente es complicado; consulte el código fuente de Apache para ver ejemplos.

Cómo solucionar esto en el caso general:

Ya sea

a) Inmediatamente después de fork (), invoque a exec (), posiblemente en el mismo binario (con los parámetros necesarios para lograr el trabajo que pretendía hacer). Esto es muy facil.

b) después de bifurcar, no use ningún asa abierta existente u objetos de la biblioteca que dependan de ellos (abrir nuevos está bien); termine su trabajo lo más rápido posible, luego llame a _exit () (no exit ()). No regrese desde la subrutina que llama a fork, ya que corre el riesgo de llamar a destructores de C ++, etc., lo que puede perjudicar los descriptores de archivos del proceso principal. Esto es moderadamente fácil.

c) Después de bifurcar, de alguna manera limpiar todos los objetos y hacer que todos estén en buen estado antes de que el niño continúe. Por ejemplo, cerrar las descripciones de archivos subyacentes sin borrar los datos que están en un buffer que está duplicado en el padre. Esto es complicado.

c) es aproximadamente lo que hace Apache.

printf() hace el almacenamiento en búfer. ¿Has intentado imprimir en stderr ?

Respuesta técnica:

cuando utilice fork (), debe asegurarse de que exit () no se invoque dos veces (la caída de main es igual a la llamada de exit ()). El niño (o rara vez el padre) necesita llamar a _exit en su lugar. Además, no use stdio en el niño. Eso es solo pedir problemas.

Algunas bibliotecas tienen un fflushall () al que puede llamar antes de fork () que hace que stdio en el niño sea seguro. En este caso particular, también haría que exit () fuera seguro, pero eso no es cierto en el caso general.