Quando vfork é chamado, o processo pai é realmente suspenso?

3

Por mais que eu saiba quando vfork é chamado de processo filho usa o mesmo espaço de endereço que o pai e quaisquer alterações feitas pelo processo filho nas variáveis pais são refletidas no processo pais. Minhas perguntas são:

  • Quando o processo filho é gerado, o processo pai é suspenso?
  • Se sim por quê?
  • Eles podem ser executados em paralelo (como Threads)? Depois que todos os threads e processos chamam a mesma função clone() .

Além disso, após um pouco de pesquisa e Goggling, descobri que o processo pai não está realmente suspenso, mas o segmento de chamada está suspenso. Mesmo se esse for o caso quando o processo filho fizer um exit() ou exec() . Como o processo pai sabe que a criança saiu? E o que acontecerá se voltarmos de um processo infantil? '

    
por Aniket Thakur 23.07.2013 / 14:24

4 respostas

2

Sua pergunta é parcialmente baseada em uma convenção de nomenclatura incorreta. Um "thread de controle" no kernel-speak é um processo no usuário. Então, quando você ler que vfork "o thread de chamada está suspenso", pense em "process" (ou "thread de alta gramatura" se quiser) e não "thread" como em "processo multi-threaded".

  • Então, sim, o processo pai está suspenso.
As semânticas de vfork foram definidas para o caso muito comum em que um processo (o shell na maioria das vezes) entraria, com alguns descritores de arquivo e, em seguida, exec de outro processo. O pessoal do kernel percebeu que poderia economizar uma enorme quantidade de sobrecarga de cópia de página se ignorasse a cópia, já que o exec iria simplesmente jogar fora essas páginas copiadas. Um vforked child tem sua própria tabela de descritores de arquivos no kernel, portanto, manipular isso não afeta o processo pai, mantendo a semântica de fork inalterada.

  • Por quê? Porque garfo / exec era comum, caro e desperdício

Dada a definição mais precisa de "thread de controle do kernel", a resposta para eles podem ser executados em paralelo é claramente

  • Não, o pai será bloqueado pelo kernel até que o filho saia ou seja executado

Como os pais sabem que a criança saiu?

  • Não, o kernel sabe e impede que o pai receba qualquer CPU até que a criança tenha ido embora.

Quanto à última questão, eu suspeitaria que o kernel detectaria as operações da pilha filho envolvidas em um retorno e sinalizaria a criança com um sinal não detectável ou apenas a mataria, mas não sei os detalhes.

    
por 23.07.2013 / 15:48
1

Eu não sou um programador linux, mas enfrentando a mesma questão hoje, fiz o seguinte teste:

#include<unistd.h>
#include<signal.h>
#include<errno.h>
#include<fcntl.h>
#include<cassert>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<iostream>
#include<fstream>
#include<sstream>
#include<list>
using namespace std;
int single_talk(int thread_id){
  fprintf(stderr,"thread %d before fork @%d\n",thread_id,time(0));
  int pid=vfork();
  if(-1==pid){
    cerr << "failed to fork: " << strerror(errno) << endl;
    _exit(-3);//serious problem, can not proceed
  }
  sleep(1);
  fprintf(stderr,"thread %d fork returned %d @%d\n",thread_id,pid,time(0));
  if(pid){//"CPP"
    fprintf(stderr,"thread %d in parent\n",thread_id);
  }else{//"PHP"
    sleep(1);
    fprintf(stderr,"thread %d in child @%d\n",thread_id,time(0));
    if(-1 == execlp("/bin/ls","ls",(char*)NULL)){
      cerr << "failed to execl php : " << strerror(errno) << endl;
      _exit(-4);//serious problem, can not proceed
    }
  }
}
void * talker(void * id){
  single_talk(*(int*)id);
  return NULL;
}
int main(){
  signal(SIGPIPE,SIG_IGN);
  signal(SIGCHLD,SIG_IGN);
  const int thread_count = 44;
  pthread_t thread[thread_count];
  int thread_id[thread_count];
  int err;
  for(size_t i=0;i<thread_count;++i){
    thread_id[i]=i;
    if((err = pthread_create(thread+i,NULL,talker,thread_id+i))){
      cerr << "failed to create pthread: " << strerror(err) << endl;
      exit(-7);
    }
  }
  for(size_t i=0;i<thread_count;++i){
    if((err = pthread_join(thread[i],NULL))){
      cerr << "failed to join pthread: " << strerror(err) << endl;
      exit(-17);
    }
  }
}

Eu compilei com g++ -pthread -o repro repro.cpp e executei com ./repro . O que vejo na saída é que tudo acontece simultaneamente em rodadas: primeiro todos os pthreads executam vfork, então todos esperam um segundo, depois "acordam" na realidade do processo filho, então todos os filhos executam exec (), e finalmente todos os pais acordam para cima.

Para mim, isso prova que se um pthread chamar um vfork, ele não suspenderá os outros pthreads - se isso acontecer, eles não poderão chamar vfork () s até que o exec () seja chamado.

    
por 23.10.2014 / 13:09
0

When child process is spawned is parent process suspended?

Sim.

If yes why?

Porque o pai e o filho estão compartilhando o espaço de endereço. Em particular, o espaço de endereço da pilha. Se o pai tentasse continuar, provavelmente chamaria outra função e trash a pilha de chamadas do processo filho. Por isso, não é executado até exec() ou _exit()

They can run in parallel (like Threads)? After all both threads and process call the same clone() function.

De fato. Apenas o segmento de chamada está suspenso. Veja acima.

How does parent process know that the child has exited?

Chama wait4() . Mas, em vez disso, você pergunta como os pais sabem continuar. Não faz. O kernel torna-o executável novamente quando um dos dois eventos definidos ocorre.

And what will happen if we return from a child process?

vfork() retorna em um quadro de pilha liberado e o próximo return; é um caminho rápido para um comportamento indefinido.

    
por 15.10.2018 / 20:11
0

What will happen if we return from a child process?

"Não      não funciona, no entanto, para retornar enquanto corre no contexto da criança de      o procedimento que chamou vfork () desde o eventual retorno de vfork ()      então retornaria a um quadro de pilha não mais existente. "

Eu não levo isso literalmente. Bytes na região da pilha não desaparecem literalmente após cada instrução POP (ou RETURN). No entanto, se você retornar, continuar correndo e executar uma instrução PUSH (ou CALL), ele substituirá o valor anterior na pilha.

É apenas um exemplo proeminente das coisas estranhas que vão acontecer em geral, se você chamar vfork() e depois fizer qualquer coisa para modificar qualquer memória do seu processo.

[Tecnicamente, assumo o mesmo comportamento que no Linux. Outros comportamentos de vfork () foram tecnicamente permitidos pelo POSIX. Eu não sei se alguém encontrou um uso para a flexibilidade técnica do POSIX, além de fornecer um vfork () que é idêntico ao fork ()].

    
por 15.10.2018 / 22:10

Tags