GNU/Linux >> Tutoriales Linux >  >> Linux

¿Por qué se pretende usar vfork() cuando el proceso hijo llama a exec() o exit() inmediatamente después de la creación?

Cuando un proceso hijo creado por vfork() llamadas exec() , no exec() modificar el espacio de direcciones del proceso padre, cargando el nuevo programa?

No, exec() proporciona un nuevo espacio de direcciones para el nuevo programa; no modifica el espacio de direcciones principal. Ver por ejemplo la discusión del exec funciones en POSIX, y Linux execve() página de manual.

Cuando un proceso secundario creado por vfork() llama a exit(), ¿exit() no modifica el espacio de direcciones del proceso principal al finalizar el proceso secundario?

Liso exit() podría:ejecuta ganchos de salida instalados por el programa en ejecución (incluidas sus bibliotecas). vfork() es más restrictivo; por lo tanto, en Linux, exige el uso de _exit() que no llame a las funciones de limpieza de la biblioteca C.

vfork() resultó ser bastante difícil de acertar; se eliminó en las versiones actuales del estándar POSIX y posix_spawn() debería usarse en su lugar.

Sin embargo, a menos que realmente sabes lo que estás haciendo, deberías no usa vfork() o posix_spawn(); manténgase en el viejo fork() y exec() .

La página de manual de Linux vinculada anteriormente proporciona más contexto:

Sin embargo, en los viejos tiempos un fork(2) requeriría hacer una copia completa del espacio de datos de la persona que llama, a menudo innecesariamente, ya que generalmente inmediatamente después aparece un exec(3) está hecho. Por lo tanto, para una mayor eficiencia, BSD introdujo el vfork() llamada al sistema, que no copió completamente el espacio de direcciones del proceso principal, pero tomó prestada la memoria del padre y el hilo de control hasta una llamada a execve(2) o se produjo una salida. El proceso padre se suspendió mientras el hijo estaba usando sus recursos. El uso de vfork() era complicado:por ejemplo, no modificar los datos en el proceso principal dependía de saber qué variables se mantenían en un registro.


Cuando llamas a vfork() , se crea un nuevo proceso y ese nuevo proceso toma prestada la imagen del proceso principal con la excepción de la pila. El proceso secundario recibe una nueva estrella de pila propia, sin embargo, no permite return de la función que llamó a vfork() .

Mientras se ejecuta el proceso secundario, el proceso principal se bloquea, ya que el secundario tomó prestado el espacio de direcciones del principal.

Independientemente de lo que haga, todo lo que solo accede a la pila modifica solo la pila privada del niño. Sin embargo, si modifica los datos globales, esto modifica los datos comunes y, por lo tanto, también afecta al padre.

Las cosas que modifican los datos globales son, por ejemplo:

  • llamando a malloc() o free()

  • usando stdio

  • modificar la configuración de la señal

  • modificando variables que no son locales a la función que llamó vfork() .

  • ...

Una vez que llames al _exit() (importante, nunca llames al exit() ), el hijo es cancelado y el control se le devuelve al padre.

Si llama a cualquier función desde el exec*() family, se crea un nuevo espacio de direcciones con un nuevo código de programa, nuevos datos y una parte de la pila del padre (ver más abajo). Una vez que esto está listo, el niño ya no toma prestado el espacio de direcciones del niño, sino que usa un espacio de direcciones propio.

El control se devuelve al padre, ya que su espacio de direcciones ya no está en uso por otro proceso.

Importante:en Linux, no existe un vfork() real implementación. Linux más bien implementa vfork() basado en Copiar al escribir fork() concepto introducido por SunOS-4.0 en 1988. Para hacer creer a los usuarios que usan vfork() , Linux simplemente configura los datos compartidos y suspende al padre mientras que el hijo no llamó a _exit() o uno de los exec*() funciones.

Por lo tanto, Linux no se beneficia del hecho de que un vfork() real no necesita configurar una descripción de espacio de direcciones para el niño en el kernel. Esto da como resultado un vfork() eso no es más rápido que fork() . En sistemas que implementan un vfork() real , normalmente es 3 veces más rápido que fork() y afecta el rendimiento de los shells que usan vfork() - ksh93 , el reciente Bourne Shell y csh .

La razón por la que nunca debes llamar al exit() del vfork() ed hijo es ese exit() vacía stdio en caso de que haya datos no vaciados desde el momento anterior a llamar a vfork() . Esto podría causar resultados extraños.

Por cierto:posix_spawn() se implementa sobre vfork() , entonces vfork() no se eliminará del sistema operativo. Se ha mencionado que Linux no usa vfork() por posix_spawn() .

Para la pila, hay poca documentación, esto es lo que dice la página del manual de Solaris:

 The vfork() and vforkx() functions can normally be used  the
 same  way  as  fork() and forkx(), respectively. The calling
 procedure, however, should not return while running  in  the
 child's  context,  since the eventual return from vfork() or
 vforkx() in the parent would be to a  stack  frame  that  no
 longer  exists. 

Entonces la implementación puede hacer lo que quiera. La implementación de Solaris usa memoria compartida para el marco de pila de la función que llama a vfork() . Ninguna implementación otorga acceso a las partes más antiguas de la pila desde el padre.


Linux
  1. ¿Nuevo proceso principal cuando muere el proceso principal?

  2. ¿Código de salida predeterminado cuando finaliza el proceso?

  3. ¿Por qué el Pgid de los procesos secundarios no es el Pid del padre?

  4. ¿Por qué Signint no se propaga al proceso secundario cuando se envía a su proceso principal?

  5. ¿Hay alguna distribución de Linux o parche del kernel que borre un espacio de memoria del proceso después de que finalice el proceso?

¿Cómo hacer que el proceso hijo muera después de que el padre salga?

¿Cómo eliminar un proceso secundario después de un tiempo de espera determinado en Bash?

¿Cuál es el significado de caddr_t y cuándo se usa?

Bash:¿Por qué la secuencia de comandos principal no termina en SIGINT cuando la secuencia de comandos secundaria atrapa a SIGINT?

¿Cómo puedo eliminar un usuario en Linux cuando el sistema dice que actualmente se usa en un proceso?

por qué se usa la lista de hermanos para obtener task_struct mientras se recuperan los elementos secundarios de un proceso