SIGSTOP が screen セッション内で機能しないのはなぜですか?

SIGSTOP が screen セッション内で機能しないのはなぜですか?

次のスクリプトを検討してください。

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

対話型シェルではスクリプトは停止し、出力は となりますok。ただし、次のように起動した場合は

screen -d -m ./test.sh

出力は ですafter stop。screen はシグナルを処理しますか? screen セッションでプロセスを一時停止するにはどうすればよいですか?

答え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 スクリプトの場合、これはサブシェルで実行できます。

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

他のシェルの場合はそれほど簡単ではないかもしれません。たとえば、tcsh の場合:

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

主な違いはサブシェルのPIDを取得する方法です。詳細はこちらここ

あるいは、スクリプトは変更せずにラッパー脚本:

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

走る:

screen -d -m ./wrapper.sh

関連情報