Shell

為 INT 設置陷阱在子 shell 中不起作用

  • November 16, 2021
$ bash -c "trap \"echo INT\" INT; sleep 3" & pid=$!; sleep 1; kill -INT $pid; wait
[1] 27811
INT
[1]+  Done                    bash -c "trap \"echo INT\" INT; sleep 3"

$ (bash -c "trap \"echo INT\" INT; sleep 3" & pid=$!; sleep 1; kill -INT $pid; wait)

你能解釋一下為什麼SIGINT處理程序在第二種情況下沒有被呼叫嗎?

作業控制是指允許使用者在單個登錄會話中在多個程序組(或作業)之間移動的協議。

https://www.gnu.org/software/libc/manual/html_node/Job-Control.html

通常它在互動式 shell 中啟用,在非互動式 shell 中禁用:

$ echo $-; sleep 1 & fg
himBHs
[1] 84366
sleep 1

$ bash -c 'echo $-; sleep 1 & fg'
hBc
bash: line 1: fg: no job control

在這種情況下……顯然工作控制被禁用,$-不能依賴:

$ (echo $-; sleep 1 & fg)
himBHs
bash: fg: no job control

Shell 將作業與每個管道相關聯。

https://www.gnu.org/software/bash/manual/html_node/Job-Control-Basics.html

也就是說,當啟用作業控制時,每個管道都在單獨的程序組中執行。

pgid.sh:

#!/usr/bin/env bash
ps -o pgid= $$
$ ./pgid.sh >&2 | ./pgid.sh >&2; ./pgid.sh; ./pgid.sh & wait
 93439
 93439
 93443
[1] 93445
 93445
[1]+  Done                    ./a.sh

$ (./pgid.sh >&2 | ./pgid.sh >&2; ./pgid.sh; ./pgid.sh & wait)
 93749
 93749
 93749
 93749

其中一項工作是前台工作,其餘工作是後台工作。

後台作業不應該綁定到啟動它們的 shell。如果您退出 shell,它們將繼續執行。因此,它們不應該被 打斷SIGINT,而不是預設情況下。當啟用作業控制時,會自動完成,因為後台作業在單獨的程序組中執行。當作業控制被禁用時,bash使非同步命令忽略SIGINT,並且不讓它們(如果它們是bash腳本)覆蓋它。

也就是說,這裡:

$ bash -c "trap 'echo INT' INT; sleep 3" & pid=$!; sleep 1; kill -INT "$pid"; wait

後台作業 ( bash -c "trap 'echo INT' INT; sleep 3") 由啟用了作業控制的互動式 shell 執行。結果,後台作業收到SIGINT.

當我們將它包裝到一個沒有作業控制的非互動式 shell 中時:

$ (bash -c "trap 'echo INT' INT; sleep 3" & pid=$!; sleep 1; kill -INT "$pid"; wait)

bash -c "trap 'echo INT' INT; sleep 3"忽略SIGINTtrap ... INT也被忽略。

可以這樣確認:

$ bash -c "trap 'echo INT' INT; trap; sleep 3" & pid=$!; sleep 1; kill -INT "$pid"; wait
[1] 293631
trap -- 'echo INT' SIGINT
trap -- '' SIGFPE
INT
[1]+  Done                    bash -c "trap 'echo INT' INT; trap; sleep 3"

$ (bash -c "trap 'echo INT' INT; trap; sleep 3" & pid=$!; sleep 1; kill -INT "$pid"; wait)
trap -- '' SIGINT
trap -- '' SIGQUIT
trap -- '' SIGFPE

$ bash -c 'ps -o pid,ignored,comm,args -p $$' & wait
[1] 345833
   PID          IGNORED COMMAND         COMMAND
345833 0000000000000000 ps              ps -o pid,ignored,comm,args -p 345833
[1]+  Done                    bash -c 'ps -o pid,ignored,comm,args -p $$'

$ (bash -c 'ps -o pid,ignored,comm,args -p $$' & wait)
   PID          IGNORED COMMAND         COMMAND
345629 0000000000000006 ps              ps -o pid,ignored,comm,args -p 345629

一些相關的報價:

由 Bash 啟動的非內置命令將信號處理程序設置為 shell 從其父級繼承的值。**當作業控制無效時,非同步命令會忽略SIGINT這些SIGQUIT**繼承的處理程序。由於命令替換而執行的命令會忽略鍵盤生成的作業控制信號SIGTTINSIGTTOUSIGTSTP

https://www.gnu.org/software/bash/manual/html_node/Signals.html

進入 shell 時忽略的信號不能被擷取或重置。

https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html#index-trap

作業控制是指有選擇地停止(暫停)程序的執行並在以後繼續(恢復)它們的執行的能力。使用者通常通過由作業系統核心的終端驅動程序和 Bash 共同提供的互動界面來使用此功能。

Shell 將作業與每個管道相關聯。它保留了一張目前正在執行的作業的表格,該表格可以與jobs命令一起列出。當 Bash 非同步啟動一個作業時,它會列印出如下所示的一行:

[1] 25647

表示此作業是作業號1,並且與此作業關聯的管道中最後一個程序的程序 ID 是25647. 單個管道中的所有程序都是同一個作業的成員。Bash 使用作業抽像作為作業控制的基礎。

為了便於實現作業控制的使用者界面,作業系統維護了目前終端程序組 ID 的概念。此程序組的成員(程序組 ID 等於目前終端程序組 ID 的程序)接收鍵盤生成的信號,例如SIGINT. 據說這些過程處於前台。**後台程序是那些程序組 ID 與終端不同的程序;這樣的過程不受鍵盤生成的信號的影響。**只允許前台程序讀取,或者,如果使用者使用 指定stty tostop,則寫入終端。嘗試從終端讀取(寫入何時stty tostop生效)的後台程序被發送一個SIGTTIN(SIGTTOU) 由核心的終端驅動程序發出的信號,除非被擷取,否則它會暫停程序。

https://www.gnu.org/software/bash/manual/html_node/Job-Control-Basics.html

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