Question

Veuillez considérer le pseudo-code fork () / SIGCHLD suivant.

  // 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);
    }
  }

Dans l'exemple ci-dessus, il existe une condition de concurrence. C'est possible pour " / * enfant * / " terminer avant "" / * parent stuff * / " commence, ce qui peut entraîner l’ajout du pid de l’enfant à la liste des enfants après sa sortie, sans jamais l’enlever. Lorsque le moment sera venu de fermer l'application, le parent attendra sans fin que l'enfant déjà terminé soit terminé.

Une solution à laquelle je peux penser pour remédier à cela est d’avoir deux listes: enfants_démarrés et enfants_finis . J'ajouterais à started_children au même endroit que j'ajoute maintenant à enfants . Mais dans le gestionnaire de signal, au lieu de supprimer de enfants , je ajouterais à enfants_finis . À la fermeture de l'application, le parent peut simplement attendre que la différence entre enfants démarrés et enfants finis soit égale à zéro.

Une autre solution à laquelle je peux penser consiste à utiliser la mémoire partagée, par exemple. partager la liste des enfants du parent et laisser les enfants .add et .remove eux-mêmes? Mais je ne sais pas trop à ce sujet.

EDIT: Une autre solution envisageable a été la première chose à faire. Il suffit simplement d’ajouter un sleep (1) au début de / * enfant * / mais ça sent drôle pour moi, c'est pourquoi je l'ai laissé de côté. Je ne suis même pas sûr que ce soit une solution à 100%.

Alors, comment corrigeriez-vous cette condition de concurrence? Et s’il existe un modèle bien établi pour cela, faites-le moi savoir!

Merci.

Était-ce utile?

La solution

La solution la plus simple consisterait à bloquer le signal SIGCHLD avant fork () avec sigprocmask () et à le débloquer dans le code parent après le traitement du pid.

Si un enfant est décédé, le gestionnaire de signal de SIGCHLD sera appelé après le déblocage du signal. C'est un concept de section critique - dans votre cas, la section critique commence avant fork () et se termine après children.add () .

Autres conseils

Si vous ne pouvez pas utiliser de fragment critique, un simple compteur peut faire ce travail. +1 quand ajouter, -1 quand supprimer, peu importe lequel arrive en premier, vous pouvez éventuellement obtenir zéro quand tout est terminé.

En plus des "enfants" existants ajouter une nouvelle structure de données "décès prématurés". Cela gardera le contenu des enfants propre.

  // 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: cela peut être simplifié si votre processus est à thread unique - earlyDeaths ne doit pas nécessairement être un conteneur, il doit simplement contenir un pid.

Peut-être un algorithme optimiste? Essayez children.remove (pid), et si cela échoue, continuez avec la vie.

Ou vérifiez que le pid est chez les enfants avant d'essayer de l'enlever?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top