2009-06-01 66 views
4

我想在Linux上創建一個包裝器,它控制一次允許多少個併發執行的東西。爲此,我正在使用系統範圍計數信號量。我創建信號量,執行sem_wait(),啓動子進程,然後在子進程結束時執行sem_post()。沒事兒。信號安全使用sem_wait()/ sem_post()

問題是如何安全地處理髮送到這個包裝的信號。如果它沒有捕獲到信號,那麼命令可能會終止而不執行sem_post(),導致信號計數永久減少1。所以,我創建了一個信號處理程序,它的作用是sem_post()。但仍然存在問題。

如果處理程序是sem_wait()連接之前進行,信號可以在sem_wait()完成之前到達,造成sem_post()沒有sem_wait()發生。如果我在設置信號處理程序之前執行sem_wait(),則可能會發生相反的情況。

明顯的下一步是在設置處理程序和sem_wait()期間阻止信號。這是我現在有僞代碼:

void handler(int sig) 
{ 
    sem_post(sem); 
    exit(1); 
} 

... 
sigprocmask(...); /* Block signals */ 
sigaction(...);  /* Set signal handler */ 
sem_wait(sem); 
sigprocmask(...); /* Unblock signals */ 
RunChild(); 
sem_post(sem); 
exit(0); 

現在的問題是,sem_wait()可以阻止在此期間,信號被阻斷。試圖殺死進程的用戶最終可能會訴諸於「kill -9」,這是我不想鼓勵的行爲,因爲無論如何我都無法處理這種情況。我可以使用sem_trywait()一小段時間,並測試sigpending()但影響公平性,因爲不再保證等待信號量最長的進程將接下來運行。

這裏有一個真正安全的解決方案,它允許我在信號量採集期間處理信號嗎?我正在考慮訴諸全球性的「我有信號量」,並取消信號阻塞,但自從獲得信號量和設置全局以來,這並非100%安全,這不是原子性的,但可能比等待時阻止信號更好。

回答

6

您確定sem_wait()導致信號被阻止嗎?我不認爲這是事實。 man page for sem_wait()表示如果EINTR錯誤代碼被信號中斷,則返回sem_wait()

你應該能夠處理這個錯誤代碼,然後你的信號將被接收。你遇到過沒有收到信號的情況嗎?

我會確保您處理sem_wait()可以返回的錯誤代碼。雖然可能很少,但如果你想100%確定你想覆蓋100%的基數。

+0

對不起,如果我不清楚。sem_wait()不會像您所說的那樣導致信號被阻塞,但我正在使用sigprocmask()來阻止它們。但是,我認爲你有正確的解決方案,即查看EINTR錯誤代碼,這意味着處理程序不應該退出,但設置一些標誌來表示「退出時間」。我會測試一下。謝謝。 – Jeremy 2009-06-01 23:46:29

+0

工作。我刪除了信號的阻塞,並將信號處理程序更改爲設置全局含義,現在是時候退出了。如果sem_wait()返回一個錯誤,那麼我退出這是很好,並保留信號計數。如果我在等孩子,我也測試timeToQuit global。如果是時候退出,我殺了孩子,並做一個sem_post(),它保留了信號計數。謝謝。這應該是信號安全的,忽略kill -9。 – Jeremy 2009-06-02 00:04:45

0

你確定你正確地解決了這個問題嗎?如果您想等待孩子終止,則可能需要使用系統調用waitpid()。正如你所觀察到的那樣,如果孩子可能接收到信號,那麼期望孩子做sem_post()是不可靠的。

+0

實際上,孩子沒有意識到信號量,父母在僞代碼中的RunChild()函數中執行了一個waitpid(),我沒有顯示。該部分沒有問題,並且正在處理在sem_take()完成後發送給父節點的子節點和信號。我擔心設置信號處理程序和sem_wait()之間的關鍵部分,但檢查sem_wait()錯誤代碼是我無法看到的簡單解決方案。 – Jeremy 2009-06-02 00:08:08

0

我知道這是舊的,但對於那些還在讀書禮貌谷歌的利益......

最簡單的(也是唯一的?)強大的解決這個問題是使用System V信號,這允許客戶端以內核自動返回的方式獲取信號量資源否否問題如何處理退出。