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.