Quando você chama vfork()
, um novo processo é criado e esse novo processo empresta a imagem do processo pai, com exceção da pilha. O processo filho recebe uma nova estrela de pilha, no entanto, não permite a return
partir da função que chamou vfork()
.
Enquanto o filho estiver em execução, o processo pai será bloqueado, pois o filho emprestou o espaço de endereço do pai.
Independentemente do que você faz, tudo o que acessa apenas a pilha modifica apenas a pilha privada da criança. Se você modificar dados globais, isso modificará os dados comuns e, portanto, também afetará o pai.
Coisas que modificam dados globais são, por exemplo:
chamando malloc () ou free ()
usando stdio
modificando configurações de sinal
modificando variáveis que não são locais para a função que chamou vfork()
.
...
Depois que você liga _exit()
(importante, nunca liga exit()
), a criança é encerrada e o controle é devolvido aos pais.
Se você chamar qualquer função da exec*()
família, um novo espaço de endereço será criado com o novo código do programa, novos dados e uma parte da pilha do pai (veja abaixo). Uma vez pronto, o filho não pede mais o espaço de endereço do filho, mas usa um próprio espaço de endereço.
O controle é devolvido ao pai, pois seu espaço de endereço não está mais sendo usado por outro processo.
Importante: No Linux, não há vfork()
implementação real . O Linux é implementado com vfork()
base no fork()
conceito Copy on Write, introduzido pelo SunOS-4.0 em 1988. Para fazer os usuários acreditarem que usam vfork()
, o Linux apenas configura dados compartilhados e suspende o pai enquanto o filho não chamava _exit()
ou uma das exec*()
funções.
Portanto, o Linux não se beneficia do fato de que um real vfork()
não precisa configurar uma descrição do espaço de endereço para o filho no kernel. Isso resulta em um vfork()
que não é mais rápido que fork()
. Em sistemas que implementam um real vfork()
, normalmente é 3x mais rápido que fork()
e afeta o desempenho de shells que usam vfork()
- ksh93
, o recente Bourne Shell
e csh
.
A razão pela qual você nunca deve ligar exit()
do vfork()
filho ed é que exit()
libera o stdio caso haja dados não liberados do momento antes da chamada vfork()
. Isso pode causar resultados estranhos.
BTW: posix_spawn()
é implementado em cima de vfork()
, portanto, vfork()
não será removido do sistema operacional. Foi mencionado que o Linux não usa vfork()
para posix_spawn()
.
Para a pilha, há pouca documentação, aqui está o que a página de manual do Solaris diz:
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.
Portanto, a implementação pode fazer o que quiser. A implementação Solaris usa memória compartilhada para o quadro de pilha da chamada de função vfork()
. Nenhuma implementação concede acesso a partes mais antigas da pilha do pai.