Bash 後台程序的信號失去
當我在 Bash 5.1 中生成一個後台作業並立即向它發送一個信號時,這個信號似乎失去了。簡短展示:
$ cat simple.cc #include <signal.h> #include <unistd.h> #include <stdlib.h> static void handler(int, siginfo_t *, void *) { write(2, "Got SIGINT\n", 11); exit(0); } int main() { struct sigaction act; act.sa_flags = SA_SIGINFO | SA_RESTART; act.sa_sigaction = handler; if (sigaction(SIGINT, &act, nullptr)) { exit(1); } while (true) {} } $ g++ -Wall -Wextra simple.cc -o simple $ ./simple & kill -SIGINT $! [1] 540434 $ # nothing happens $ $ kill -SIGINT 540434 Got SIGINT
我的假設是,在信號觸發時,分叉的後台程序仍然執行 Bash。Bash 嘗試將 SIGINT 轉發到前台程序,可惜沒有,因此 SIGINT 被丟棄。
我的問題:
- 這個對嗎?我如何驗證這是實際發生的情況?
- 假設我無法修改要在後台執行的程序。如何確保它已經處於活動狀態並會處理信號?一個簡單的
sleep 1
在這裡會有所幫助,但我正在尋找適當的同步。請注意,我不關心是否設置了預期的信號處理程序,只關心正確的二進製文件執行。
注意:這個答案需要
/proc
.$$ … $$SIGINT 被丟棄。$$ … $$這個對嗎?
基本上是的,儘管我不確定您的描述是否準確(“轉發 SIGINT”?)。當主(在您的範例中:互動式)
bash
分叉並且行的執行超出&
時,有一個帶有 PID 的程序$!
並且信號到達它。問題是這個過程最初是另一個bash
即將到來exec
的./simple
。顯然是對方bash
收到了信號。雖然它沒有退出,用 替換自己./simple
,但是信號已經“用完”了。我既不是程序員也不是 *nix 專家。我不確定我的描述是否準確。即使它不完全準確,這個答案的其餘部分也可能有用。
我如何驗證這是實際發生的情況?
可能發生問題的時間視窗非常狹窄。
kill
通常延遲“解決”問題就足夠了。從理論上講,任何延遲,無論有多大,都可能在特定嘗試中變得不夠大。當您撰寫有關“正確同步”的文章時,您似乎明白這一點。
kill
是 Bash 中的內置函式,它的速度足以觸發問題。可以使用外部kill
(例如/bin/kill
)。它是作為一個單獨的過程產生的,這需要時間,在我的測試中它從未觸發過問題。我試圖在另一個
bash
替換為./simple
;之前抓住它。我試圖檢查/proc/$!/exe
。不幸的是ls -l
或者readlink
太慢了,它們不是內置的。一個有用的內置函式 is
test
或其同義詞[
。我bash
是/bin/bash
,我可以這樣做:./simple & [ "/proc/$!/exe" -ef /bin/bash ] && echo gotcha
(在測試這個時,不要忘記
killall simple
這樣做。)請參閱help test
Bash 以了解其-ef
作用。
bash
讓我們看看在變成之前我可以執行多少測試simple
:./simple & while [ "/proc/$!/exe" -ef /bin/bash ]; do echo a; done
在我的測試中,回顯字元串的數量約為 5。最初我試圖通過管道來計算它們,
wc -l
但這引入了延遲,結果是0
.主程式碼繼續,後台程序還沒有的時間視窗
./simple
確實很窄,但肯定是存在的。假設我無法修改要在後台執行的程序。如何確保它已經處於活動狀態並會處理信號?
$$ … $$請注意,我不關心是否設置了預期的信號處理程序,只關心正確的二進製文件執行。
循環測試
/proc/$!/exe
。bash
變為後退出循環simple
。測試不必非常快。請注意:
while [ "/proc/$!/exe" -ef /bin/bash ]; do :; done
硬編碼/bin/bash
。until [ "/proc/$!/exe" -ef ./simple ]; do :; done
如果simple
退出速度足夠快或從未執行(例如由於某些錯誤),將無限循環。我認為這是一個合理的方法:
./simple & while [ "/proc/$!/exe" -ef "/proc/$$/exe" ]; do :; done; kill -s INT "$!"
在我的測試中,信號
simple
在處理程序設置之前一直到達;每次主外殼稍後通知我時,工作都會中斷。您寫道“我不在乎是否設置了預期的信號處理程序,只關心正確的二進製文件執行”。所以,是的,這就是你可以做到的。