親プログラム(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
答え2
POSIX で引用した章で説明したように、SIGINT はフォアグラウンドプロセスグループ全体に転送されます。したがって、プログラムの親プロセスが終了するのを防ぐために、独自のプロセスグループで実行するようにスケジュールしてください。
シェルsetpgrp
には組み込みまたは構文構成ではアクセスできませんが、シェルを対話的に実行する間接的な方法があります。 (ありがとうございます。スティーブン・ヒメネス技術のため。 )
ksh -ic '
… the part that needs to be interruptible without bothering the parent …
'
答え3
まあ、あなたが参照しているスタックオーバーフローの質問から信号を処理するように親エントリを設定する必要があることを明確に示します。
第二に、POSIX リファレンスでは、「ISIG が設定されている場合は INTR 文字を処理から捨てなければなりません」と明確に明記されています。
2つのオプションがあります。 3番目の方法は、独自のプロセスグループで子プロセスを実行することです。