親プログラム(C ++プログラムまたはシェルスクリプトのいずれか)がサブシェルスクリプトを実行するシナリオを考えてみましょう。サブシェルスクリプトの実行中にCtrl + C(またはINTR文字で構成される文字)を押すと、SIGINTは次のようになります。のすべてのプロセスをフォアグラウンドプロセスグループに送信します。これには親プロセスが含まれます。
源泉:POSIX.1-2008 XBDセクション11.1.9
このデフォルトの動作をオーバーライドする方法はありますか?子プロセスはシグナルを親プロセスに伝播せずに単独で処理しますか?
ベストアンサー1
(Gilesの答えからインスピレーションを得た)
フラグが設定されている場合、スクリプトが親プロセスをインポートせずにインポートする唯一の方法は、ISIG
独自のプロセスグループにスクリプトを配置することです。これはオプションで行うことができます。Child
SIGINT
SIGINT
set -m
-m
シェルスクリプトでこのオプションがオンになっている場合は、対話なしでジョブChild
制御を実行します。これにより、別のプロセスグループでジョブが実行され、親プロセスがSIGINT
読み取ったときに文字を受信できなくなります。INTR
ここにいる-m
POSIX オプションの説明:
-m
実装がユーザー移植性ユーティリティオプションをサポートしている場合は、このオプションをサポートする必要があります。すべてのジョブは独自のプロセスグループで実行する必要があります。バックグラウンドジョブが完了した直後にシェルプロンプトが表示される前に、バックグラウンドジョブの終了ステータスを報告するメッセージを標準エラーに記録する必要があります。フォアグラウンドジョブが停止すると、シェルはジョブユーティリティで説明されている形式で標準エラーに関するメッセージを作成する必要があります。また、ジョブが終了せずに状態が変更された場合(たとえば、入力または出力が停止した場合、またはSIGSTOP信号によって停止した場合など)、シェルは次のプロンプトを作成する直前に同様のメッセージを作成する必要があります。デフォルトでは、対話型シェルではこのオプションが有効になっています。
この-m
オプションは似ています-i
が、シェルの動作をほとんど大きく変更しません-i
。
例:
スクリプト
Parent
:#!/bin/sh trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT echo "PARENT: pid=$$" echo "PARENT: Spawning child..." ./Child echo "PARENT: child returned" echo "PARENT: exiting normally"
スクリプト
Child
:#!/bin/sh -m # ^^ # notice the -m option above! trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT echo "CHILD: pid=$$" echo "CHILD: hit enter to exit" read foo echo "CHILD: exiting normally"
入力を待っている間に+を押すと、次のControlことが起こります。CChild
$ ./Parent
PARENT: pid=12233
PARENT: Spawning child...
CHILD: pid=12234
CHILD: hit enter to exit
^CCHILD: caught SIGINT; exiting
PARENT: child returned
PARENT: exiting normally
SIGINT
親ハンドラがどのように動作しないかを確認してください。
Parent
または代わりに変更するには、Child
次のようにします。
スクリプト
Parent
:#!/bin/sh trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT echo "PARENT: pid=$$" echo "PARENT: Spawning child..." sh -m ./Child # or 'sh -m -c ./Child' if Child isn't a shell script echo "PARENT: child returned" echo "PARENT: exiting normally"
スクリプト
Child
(通常、必須ではありません-m
):#!/bin/sh trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT echo "CHILD: pid=$$" echo "CHILD: hit enter to exit" read foo echo "CHILD: exiting normally"
代替アイデア
SIGINT
期間中に無視されるように、フォアグラウンドプロセスグループの他のプロセスを変更しますChild
。これで問題は解決しませんが、欲しいものを手に入れることができます。- に変更
Child
:stty -g
現在の端末設定をバックアップするために使用されます。- 、および文字を含む信号は実行
stty -isig
時に生成されません。INTR
QUIT
SUSP
- バックグラウンドで端末入力を読み取り、必要に応じて信号自体を送信します(例:
kill -QUIT 0
読み取り+で実行、読み取り+で実行)。これは些細な問題ではなく、スクリプトや実行中のすべての項目が対話型である場合、スムーズに機能しない可能性があります。Control\kill -INT $$
ControlCChild
- 終了する前に端末設定を復元してください(好ましくは上記
EXIT
)。
- を実行する代わりに、
stty -isig
ユーザーが殺す前Enterに押すか、他の非特殊キーを押すのを待つことを除いて、#2と同じですChild
。 setpgid
setpgid()
以下はおおよそのC実装です。#define _XOPEN_SOURCE 700 #include <unistd.h> #include <signal.h> int main(int argc, char *argv[]) { // todo: add error checking void (*backup)(int); setpgid(0, 0); backup = signal(SIGTTOU, SIG_IGN); tcsetpgrp(0, getpid()); signal(SIGTTOU, backup); execvp(argv[1], argv + 1); return 1; }
使用例
Child
:#!/bin/sh [ "${DID_SETPGID}" = true ] || { # restart self after calling setpgid(0, 0) exec env DID_SETPGID=true setpgid "$0" "$@" # exec failed if control reached this point exit 1 } unset DID_SETPGID # do stuff here