Pergunta

Por favor, considere o seguinte / fork() pseudo-código SIGCHLD.

  // main program excerpt
    for (;;) {
      if ( is_time_to_make_babies ) {

        pid = fork();
        if (pid == -1) {
          /* fail */
        } else if (pid == 0) {
          /* child stuff */
          print "child started"
          exit
        } else {
          /* parent stuff */
          print "parent forked new child ", pid
          children.add(pid);
        }

      }
    }

  // SIGCHLD handler
  sigchld_handler(signo) {
    while ( (pid = wait(status, WNOHANG)) > 0 ) {
      print "parent caught SIGCHLD from ", pid
      children.remove(pid);
    }
  }

No exemplo acima, há uma corrida de condição. É possível que "/* child stuff */" ao fim antes do início da "/* parent stuff */", que pode resultar em ser pid de uma criança adicionado à lista das crianças depois que saiu, e nunca sendo removido. Quando chega a hora para o fechamento do app para baixo, o pai vai esperar indefinidamente para a criança já terminou ao fim.

Uma solução que eu posso pensar para combater isso é ter duas listas: started_children e finished_children. Eu adicionaria a started_children no mesmo lugar que eu estou adicionando a children agora. Mas no manipulador de sinal, em vez de remover a partir children eu Adicionar para finished_children. Quando o aplicativo fecha, o pai pode simplesmente esperar até que a diferença entre started_children e finished_children é zero.

Outra solução possível que eu posso pensar é usando de memória compartilhada, por exemplo, compartilhar lista dos pais das crianças e deixar que o .add crianças e .remove si mesmos? Mas eu não sei muito sobre isso.

EDIT: Outra solução possível, que foi a primeira coisa que me veio à mente, é simplesmente adicionar um sleep(1) no início do /* child stuff */ mas que cheira engraçado para mim, é por isso que eu deixei-o para fora. Estou também nem mesmo certeza de que é uma correção de 100%.

Então, como você corrigir esta corrida condição? E se há um padrão recomendado bem estabelecido para isso, por favor me avise!

Graças.

Foi útil?

Solução

solução mais simples seria sinal de bloqueio SIGCHLD antes fork() com sigprocmask() e desbloqueá-lo no código do pai depois de ter processado o pid.

Se a criança morreu, manipulador de sinal para SIGCHLD será chamado depois de desbloquear o sinal. É um conceito seção crítica -. No seu caso seção começa críticos antes fork() e termina após children.add()

Outras dicas

Se você não pode usar fragmento crítica, talvez um contador simples podem fazer este trabalho. +1 quando add, -1 quando remove, não mater qual acontecer primeiro, você eventualmente pode obter zero quando tudo está feito.

Além das "crianças" existentes adicionar uma nova estrutura de dados "mortes precoces". Isto irá manter o conteúdo de crianças limpo.

  // main program excerpt
    for (;;) {
      if ( is_time_to_make_babies ) {

        pid = fork();
        if (pid == -1) {
          /* fail */
        } else if (pid == 0) {
          /* child stuff */
          print "child started"
          exit
        } else {
          /* parent stuff */
          print "parent forked new child ", pid
          if (!earlyDeaths.contains(pid)) {
              children.add(pid);
          } else {
              earlyDeaths.remove(pid);
          }
        }

      }
    }

  // SIGCHLD handler
  sigchld_handler(signo) {
    while ( (pid = wait(status, WNOHANG)) > 0 ) {
      print "parent caught SIGCHLD from ", pid
      if (children.contains(pid)) {
          children.remove(pid);
      } else {
          earlyDeaths.add(pid);
      }
    }
  }

EDIT: isto pode ser simplificado se o seu processo é único segmento - não earlyDeaths não tem que ser um recipiente, ele só tem que segurar um pid

.

Talvez um algoritmo otimista? Tente children.remove (PID), e se ele falhar, seguir em frente com a vida.

Ou verificar que pid é em crianças antes de tentar removê-lo?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top