為什麼 SIGSTOP 在螢幕會話內不起作用?

為什麼 SIGSTOP 在螢幕會話內不起作用?

考慮以下腳本:

#!/bin/bash
OFILE='log'
echo 'ok' > ${OFILE}
kill -SIGSTOP $$
echo 'after stop' > ${OFILE}

在互動式 shell 中,腳本停止並且輸出為ok.但是,如果它啟動為

screen -d -m ./test.sh

輸出是after stop.螢幕處理訊號嗎?如何在螢幕會話中暫停進程?

答案1

也許 screen 進程正在重新啟動停止的 bash 進程。我嘗試了以下操作(向兩個進程發送 SIGSTOP 信號):

測試文件

#!/bin/bash
OFILE='log'
echo 'ok' > ${OFILE}
echo 'screen pid ' $(screen -list | grep sc_test | cut -f1 -d'.' | sed 's/\W//g') >> ${OFILE}
echo 'test.sh pid ' $$ >> ${OFILE}
kill -SIGSTOP $(screen -list | grep sc_test | cut -f1 -d'.' | sed 's/\W//g')
kill -SIGSTOP $$
echo 'after stop' >> ${OFILE}

螢幕指令

screen -dmS sc_test ./test.sh

記錄檔

ok
screen pid  4453
test.sh pid  4454

列表螢幕

screen -list
There is a screen on:
        4453.sc_test    (11/05/2015 10:45:20 AM)        (Detached)
1 Socket in /var/run/screen/S-root.

答案2

的檢查GNU screen-4.0.2 原始碼顯示 screen 檢查其子進程是否停止,然後嘗試使用 SIGCONT 還原它們。這是相關部分screen.c

1561 if (WIFSTOPPED(wstat))
1562   {   
1563     debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, p->w_pid, WSTOPSIG(wstat));
....
1578     /* Try to restart process */
1579     Msg(0, "Child has been stopped, restarting.");
1580     if (killpg(p->w_pid, SIGCONT))
1581       kill(p->w_pid, SIGCONT);
1582   }   

似乎無法透過選項或設定檔更改此行為。建議的停止螢幕處理的解決方案可能會產生不良的副作用。更好的方法是從螢幕上隱藏停止的進程。對於 bash 腳本,可以使用子 shell 來完成:

#!/bin/bash
(   
OFILE='log'
echo 'ok' > ${OFILE}
kill -SIGSTOP ${BASHPID}
echo 'after stop' > ${OFILE}
)   

對於其他 shell 可能不那麼簡單,例如對於 tcsh:

#!/bin/tcsh
(\  
set OFILE='log' ;\
echo 'ok' > ${OFILE} ;\
kill -STOP `exec sh -c 'echo ${PPID}'` ;\
echo 'after stop' > ${OFILE} \
)   

主要區別在於獲取子shell PID的方法,更多信息這裡

或者,可以在不修改的情況下啟動腳本包裝紙腳本:

#!/bin/bash
SCRIPTDIR="$(dirname "$(readlink -f -n "$0")")"
${SCRIPTDIR}/test.sh

跑步:

screen -d -m ./wrapper.sh

相關內容