Bash

Bash 後台程序的信號失去

  • December 4, 2020

當我在 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 被丟棄。

我的問題:

  1. 這個對嗎?我如何驗證這是實際發生的情況?
  2. 假設我無法修改要在後台執行的程序。如何確保它已經處於活動狀態並會處理信號?一個簡單的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太慢了,它們不是內置的。

一個有用的內置函式 istest或其同義詞[。我bash/bin/bash,我可以這樣做:

./simple & [ "/proc/$!/exe" -ef /bin/bash ] && echo gotcha

(在測試這個時,不要忘記killall simple這樣做。)請參閱help testBash 以了解其-ef作用。

bash讓我們看看在變成之前我可以執行多少測試simple

./simple & while [ "/proc/$!/exe" -ef /bin/bash ]; do echo a; done

在我的測試中,回顯字元串的數量約為 5。最初我試圖通過管道來計算它們,wc -l但這引入了延遲,結果是0.

主程式碼繼續,後台程序還沒有的時間視窗./simple確實很窄,但肯定是存在的。


假設我無法修改要在後台執行的程序。如何確保它已經處於活動狀態並會處理信號?

$$ … $$請注意,我不關心是否設置了預期的信號處理程序,只關心正確的二進製文件執行。

循環測試/proc/$!/exebash變為後退出循環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在處理程序設置之前一直到達;每次主外殼稍後通知我時,工作都會中斷。您寫道“我不在乎是否設置了預期的信號處理程序,只關心正確的二進製文件執行”。所以,是的,這就是你可以做到的。

引用自:https://unix.stackexchange.com/questions/622763