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.