GNU/Linux >> Tutoriales Linux >  >> Linux

¿Qué sucede cuando un hilo se bifurca?

El nuevo proceso será el hijo del subproceso principal que creó el subproceso. creo.

fork crea un nuevo proceso. El padre de un proceso es otro proceso, no un hilo. Entonces, el padre del nuevo proceso es el proceso anterior.

Tenga en cuenta que el proceso secundario solo tendrá un hilo porque fork solo duplica el hilo (la pila para el) que llama a fork . (Esto no es del todo cierto:toda la memoria está duplicada, pero el proceso secundario solo tendrá un subproceso activo).

Si su padre finaliza primero, el nuevo proceso se adjuntará al proceso de inicio.

Si el padre termina primero un SIGHUP se envía una señal al niño. Si el niño no sale como resultado del SIGHUP obtendrá init como su nuevo padre. Consulte también las páginas del manual para nohup y signal(7) para un poco más de información sobre SIGHUP .

Y su padre es el hilo principal, no el hilo que lo creó.

El padre de un proceso es un proceso, no un subproceso específico, por lo que no tiene sentido decir que el subproceso principal o secundario es el padre. Todo el proceso es el padre.

Una nota final:la mezcla de hilos y tenedor debe hacerse con cuidado. Algunas de las trampas se discuten aquí.


Corrígeme si me equivoco.

Lo haré :)

Como fork() es una llamada al sistema POSIX, su comportamiento está bien definido:

Se debe crear un proceso con un único subproceso . Si un proceso de subprocesos múltiples llama a fork(), el nuevo proceso contendrá una réplica del subproceso de llamada y todo su espacio de direcciones, posiblemente incluidos los estados de mutexes y otros recursos. En consecuencia, para evitar errores, el proceso secundario solo puede ejecutar operaciones seguras de señal asíncrona hasta que se llame a una de las funciones exec.

https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html

Un hijo bifurcado es un duplicado exacto de su padre, pero solo el hilo que llamó fork() en el padre, todavía existe en el hijo y es el nuevo hilo principal de ese hijo hasta que llame a exec() .

La descripción POSIX "se creará con un solo subproceso" es engañosa, ya que, de hecho, la mayoría de las implementaciones realmente crearán un duplicado exacto del proceso principal, por lo que todos los demás subprocesos y su memoria también se duplican, lo que significa que los subprocesos están de hecho allí. , simplemente ya no pueden ejecutarse ya que el sistema nunca les asigna ningún tiempo de CPU; de hecho, faltan en la tabla del programador de subprocesos del kernel.

Una imagen mental más fácil es la siguiente:

Cuando el padre llama a fork, todo el proceso se congela por un momento, se duplica atómicamente, y luego el padre se descongela como un todo, pero en el hijo solo se descongela el hilo que llamó fork, todo lo demás permanece congelado.

Es por eso que no es seguro realizar ciertas llamadas al sistema entre fork() y exec() como también lo señala el estándar POSIX. Idealmente, no debería hacer mucho más que cerrar o duplicar descriptores de archivos, configurar o restaurar controladores de señales y luego llamar a exec() .


Sin embargo, ¿qué pasará si un hilo crea un nuevo proceso usando fork()?

Se creará un nuevo proceso copiando el hilo de llamada espacio de direcciones (no todo el espacio de direcciones del proceso ). Generalmente se considera una mala idea porque es muy difícil hacerlo bien. POSIX dice que el proceso secundario (creado en un programa de subprocesos múltiples) solo puede llamar a funciones seguras de señal asíncrona hasta que llame a uno de los exec* funciones.

Si su padre finaliza primero, el nuevo proceso se adjuntará a initprocess.

El proceso hijo normalmente lo hereda el proceso init. Si el proceso principal es un proceso de control (por ejemplo, shell), entonces POSIX requiere:

Si el proceso es un proceso de control, la señal SIGHUP se enviará a cada proceso en el grupo de procesos de primer plano del terminal de control perteneciente al proceso de llamada.

Sin embargo, esto no es cierto para la mayoría de los procesos, ya que la mayoría de los procesos no son procesos de control.

Y su padre es el hilo principal, no el hilo que lo creó.

El padre del niño bifurcado siempre será el proceso que llamó fork(). Entonces, PPID es el proceso hijo será el PID de su programa.


El problema surge del comportamiento de fork(2) en sí. Cada vez que se crea un nuevo proceso secundario con fork(2), el nuevo proceso obtiene un nuevo espacio de direcciones de memoria, pero todo lo que hay en la memoria se copia del proceso anterior (con la copia en escritura eso no es 100 % cierto, pero la semántica es la misma).

Si llamamos a fork(2) en un entorno de subprocesos múltiples, el subproceso que realiza la llamada ahora es el subproceso principal en el nuevo proceso y todos los demás subprocesos, que se ejecutaron en el proceso principal, están muertos. Y todo lo que hicieron quedó exactamente como estaba justo antes de la llamada a fork(2).

Ahora imagine que estos otros subprocesos estaban felizmente haciendo su trabajo antes de la llamada a fork(2) y un par de milisegundos después están muertos. ¿Qué pasa si algo que hicieron estos hilos ahora muertos no estaba destinado a ser dejado exactamente como estaba?

Dejame darte un ejemplo. Digamos que nuestro subproceso principal (el que se llamará fork(2)) estaba durmiendo mientras teníamos muchos otros subprocesos felizmente trabajando. Asignando memoria, escribiendo en ella, copiando de ella, escribiendo en archivos, escribiendo en una base de datos, etc. Probablemente estaban asignando memoria con algo como malloc(3). Bueno, resulta que malloc(3) usa un mutex internamente para garantizar la seguridad de subprocesos. Y exactamente este es el problema.

¿Qué pasa si uno de estos subprocesos estaba usando malloc(3) y adquirió el bloqueo del mutex exactamente en el mismo momento en que el subproceso principal llamó fork(2)? En el nuevo proceso secundario, el bloqueo aún se mantiene, por un subproceso ahora inactivo, que nunca lo devolverá.

El nuevo proceso hijo no tendrá idea de si es seguro usar malloc(3) o no. En el peor de los casos, llamará a malloc(3) y bloqueará hasta que adquiera el bloqueo, lo que nunca sucederá, ya que el hilo que se supone que debe devolverlo está muerto. Y esto es solo malloc(3). Piense en todos los demás mutex y bloqueos posibles en controladores de bases de datos, bibliotecas de manejo de archivos, bibliotecas de redes, etc.

para una explicación completa puede ir a través de este enlace.


Linux
  1. Linux:¿cuándo no debo matar -9 un proceso?

  2. ¿Qué sucede cuando ejecuto el comando Cat /proc/cpuinfo?

  3. ¿Qué sucede exactamente cuando ejecuto un archivo en el Shell?

  4. Linux:¿qué sucede cuando ejecuta Rsync sin una ruta de destino?

  5. ¿Qué es un proceso ininterrumpido?

¿Qué hace un programa cuando se envía una señal Sigkill?

¿Qué sucede cuando se excede el límite de ancho de banda?

¿Qué sucede si se interrumpe mv?

¿Qué es un proceso detenido en Linux?

¿Qué es un dispositivo de bucle cuando se monta?

¿Qué sucede cuando otro proceso modifica un archivo que está paginado al 100 % en la memoria caché de la página?