A página man do Linux para vork
é bem específica:
vfork()
differs fromfork(2)
in that the calling thread is suspended until the child terminates
Não é todo o processo, mas, de fato, o thread de chamada. Esse comportamento não é garantido pelo POSIX ou outros padrões, outras implementações podem fazer coisas diferentes (até e inclusive simplesmente implementando vfork
com um fork
simples).
(Rich Felker também observa esse comportamento em vfork considerado perigoso .)
Usar fork
em um programa multi-thread é difícil o suficiente para já estar em causa, chamando vfork
é pelo menos tão ruim. Seus testes estão cheios de comportamento indefinido, você nem está autorizado a chamar uma função (muito menos E / S) dentro da criança vfork
'd, exceto as funções exec
-type e _exit
(nem mesmo exit
e retornando causa caos).
Aqui está um exemplo adaptado do seu, que acredito ser quase livre de comportamento indefinido, supondo um compilador / implementação que não produza chamadas de função para leituras e gravações atômicas em int
s. (O único problema é a gravação em start
após o vfork
- isso não é permitido.) Manipulação de erros elidida para mantê-lo curto.
#include<unistd.h>
#include<signal.h>
#include<errno.h>
#include<atomic>
#include<cstring>
#include<string>
#include<iostream>
std::atomic<int> start;
std::atomic<int> counter;
const int thread_count = 4;
void *vforker(void *){
std::cout << "vforker starting\n";
int pid=vfork();
if(pid == 0){
start = 1;
while (counter < (thread_count-1))
;
execlp("/bin/date","date",nullptr);
}
std::cout << "vforker done\n";
return nullptr;
}
void *job(void *){
while (start == 0)
;
counter++;
return NULL;
}
int main(){
signal(SIGPIPE,SIG_IGN);
signal(SIGCHLD,SIG_IGN);
pthread_t thread[thread_count];
counter = 0;
start = 0;
pthread_create(&(thread[0]), nullptr, vforker, nullptr);
for(int i=1;i<thread_count;++i)
pthread_create(&(thread[i]), nullptr, job, nullptr);
for(int i=0;i<thread_count;++i)
pthread_join(thread[i], nullptr);
}
A idéia é a seguinte: os threads normais aguardam (busy-loop) para que a variável global atômica start
seja 1
antes de incrementar um contador atômico global. O encadeamento que faz um vfork
define start
para 1
no filho do vfork, então espera (ocupado-loop novamente) pelos outros encadeamentos para ter incrementado o contador.
Se os outros tópicos estivessem suspensos durante vfork
, nenhum progresso poderia ser feito: os threads suspensos nunca aumentariam counter
(eles teriam sido suspensos antes que start
fosse definido como 1
), o thread de vforker ficaria preso em uma infinita espera ocupada.