Вопрос

Пожалуйста, примите во внимание следующее fork()/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);
    }
  }

В приведенном выше примере есть условие гонки.Это возможно для "/* child stuff */" закончить до "/* parent stuff */" запуски, которые могут привести к тому, что pid дочернего элемента будет добавлен в список дочерних элементов после его выхода и никогда не будет удален.Когда придет время закрывать приложение, родитель будет бесконечно ждать завершения работы уже готового дочернего устройства.

Одно из решений, которое я могу придумать, чтобы противостоять этому, - иметь два списка: started_children и finished_children.Я бы добавил к started_children в том же месте, которое я добавляю к children сейчас же.Но в обработчике сигнала, вместо удаления из children Я бы Добавить Для finished_children.Когда приложение закрывается, родитель может просто подождать, пока не исчезнет разница между started_children и finished_children равен нулю.

Другое возможное решение, о котором я могу подумать, - это использование общей памяти, напримерподелитесь родительским списком дочерних элементов и позвольте дочерним элементам .add и .remove сами по себе?Но я не слишком много знаю об этом.

Редактировать:Другое возможное решение, которое было первым, что пришло на ум, - это просто добавить sleep(1) в начале /* child stuff */ но для меня это странно пахнет, вот почему я опустил это.Я также даже не уверен, что это 100% исправление.

Итак, как бы вы исправили это состояние гонки?И если для этого есть устоявшийся рекомендуемый шаблон, пожалуйста, дайте мне знать!

Спасибо.

Это было полезно?

Решение

Самым простым решением было бы заблокировать сигнал SIGCHLD перед fork() с sigprocmask() и разблокируйте его в родительском коде после того, как вы обработаете pid.

Если дочерний элемент умер, обработчик сигнала для SIGCHLD будет вызван после того, как вы разблокируете сигнал.Это концепция критического раздела - в вашем случае критический раздел начинается раньше fork() и заканчивается после children.add().

Другие советы

Если вы не можете использовать критический фрагмент, возможно, простой счетчик сможет выполнить эту работу.+ 1 при добавлении, -1 при удалении, независимо от того, какой из них произойдет первым, вы в конечном итоге можете получить ноль, когда все будет сделано.

В дополнение к существующему "дети" добавьте новую структуру данных "ранние смерти".Это позволит сохранить содержимое детских сумок в чистоте.

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

Редактировать:это можно упростить, если ваш процесс однопоточный - earlyDeaths не обязательно должен быть контейнером, он просто должен содержать один pid.

Может быть, оптимистичный алгоритм?Попробуйте children.remove (pid), и если это не удастся, двигайтесь дальше по жизни.

Или проверьте, что pid находится в дочерних элементах, прежде чем пытаться удалить его?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top