簡體   English   中英

避免fork()/ SIGCHLD競爭條件

[英]Avoiding a fork()/SIGCHLD race condition

請考慮以下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_childrenfinished_children 我會在我現在添加到children的同一個地方添加到started_children 但是在信號處理程序中,我將添加finished_children而不是從children節點中刪除。 當應用關閉時,父母可以等待,直到started_childrenfinished_children之間的差異為零。

我能想到的另一種可能的解決方案是使用共享內存,例如,共享父項的子項列表,並讓子項.add.remove自己? 但我對此並不太了解。

編輯:另一個可能的解決方案,這是我想到的第一件事,就是簡單地在/* child stuff */開始添加一個sleep(1) /* child stuff */但這對我來說很有趣,這就是我把它留下來的原因。 我甚至不確定它是100%修復。

那么,你如何糾正這種競爭條件? 如果有一個完善的推薦模式,請告訴我!

謝謝。

最簡單的解決方案是使用sigprocmask()fork()之前阻塞SIGCHLD信號,並在處理完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 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM