Cuando llama vfork()
, se crea un nuevo proceso y ese nuevo proceso toma prestada la imagen del proceso padre con la excepción de la pila. El proceso hijo recibe una nueva estrella de pila, sin embargo, no permite return
desde la función que llamó vfork()
.
Mientras el niño se está ejecutando, el proceso padre se bloquea, ya que el niño tomó prestado el espacio de direcciones del padre.
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 gratis ()
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 llame _exit()
(importante, nunca llame exit()
), el niño termina y el control se devuelve al padre.
Si llama a cualquier función de la exec*()
familia, 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 hay una vfork()
implementación real . Linux implementa más bien vfork()
basado en el fork()
concepto Copy on Write introducido por SunOS-4.0 en 1988. Para hacer que los usuarios crean que usan vfork()
, Linux simplemente configura datos compartidos y suspende al padre mientras el niño no llamó _exit()
o una de las exec*()
funciones.
Por lo tanto, Linux no se beneficia del hecho de que un real vfork()
no necesita configurar una descripción de espacio de direcciones para el niño en el núcleo. Esto resulta en un vfork()
que no es más rápido que fork()
. En los sistemas que implementan un real vfork()
, generalmente es 3 veces más rápido 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 debe llamar exit()
desde el elemento vfork()
secundario ed es que exit()
elimina stdio en caso de que haya datos no reflejados del momento anterior a la llamada vfork()
. Esto podría causar resultados extraños.
Por cierto: posix_spawn()
se implementa además vfork()
, por vfork()
lo que no se eliminará del sistema operativo. Se ha mencionado que Linux no usa vfork()
para posix_spawn()
.
Para la pila, hay poca documentación, esto es lo que dice la página de 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 la pila de la función que llama vfork()
. Ninguna implementación otorga acceso a las partes más antiguas de la pila desde el padre.
posix_spawn
. Es significativamente más difícil escribir el código correcto usandoposix_spawn
que con el viejofork
, y si lo intentas, puedes toparte con la pared de ladrillos de que no haya una acción o atributo de archivo que haga lo que necesitas hacer entrefork
yexec
. Y no se garantiza que tenga una eficiencia similar a la de vfork, por lo que ni siquiera resuelve el problema que la gente quiere que resuelva.posix_spawn
puede carecer de la funcionalidad que desea (puede resolver esto a través de un programa auxiliar intermedio, escrito en C o script de shell inline-on-cmdline), cualquier intento de lograr lo que desea convfork
invoca un comportamiento indefinido peligroso. La especificación paravfork
no permite llamar a funciones aleatorias para configurar el estado para que el hijo herede antesexecve
, y los intentos de hacerlo pueden dañar el estado del padre.posix_spawn
realiza aproximadamente lo mismo quevfork
en la mayoría de las condiciones. Los casos en los que hay una diferencia tienden a ser exactamente los casos en los quevfork
es altamente inseguro: donde hay controladores de señales instalados que deben evitar queposix_spawn
se ejecuten en el niño antes de la ejecución.