web-dev-qa-db-ja.com

親プロセスへのSIGINTの伝播の防止

Control + C(またはINTR character )子シェルスクリプトの実行中に、SIGINTがフォアグラウンドプロセスグループのすべてのプロセスに送信されます。これには、親プロセスが含まれます。

出典: POSIX.1-2008 XBDセクション11.1.9

このデフォルトの動作をオーバーライドする方法はありますか?子プロセスだけが親に伝播せずに信号を処理することですか?

参照: スタックオーバーフローポスト-子が中断されたときに親プロセスが完了しない(TRAP INT)

10
Guddu

(ジルの答えに触発されて)

ISIGフラグが設定されている場合、親がChildを取得せずにSIGINTスクリプトがSIGINTを取得する唯一の方法は、独自のプロセス内にあることです。グループ。これは、_set -m_オプションで実行できます。

Childシェルスクリプトで_-m_オプションをオンにすると、対話型でなくてもジョブ制御が実行されます。これにより、別のプロセスグループで実行され、SIGINT文字が読み取られたときに親がINTRを受信できなくなります。

_-m_オプションのPOSIX説明

_-m_このオプションは、実装がUser Portability Utilitiesオプションをサポートしている場合にサポートされます。すべてのジョブは、独自のプロセスグループで実行されます。バックグラウンドジョブの完了後にシェルがプロンプトを発行する直前に、バックグラウンドジョブの終了ステータスを報告するメッセージが標準エラーに書き込まれます。フォアグラウンドジョブが停止した場合、シェルはその結果を標準エラーにメッセージとして書き込み、ジョブユーティリティによって記述された形式になっています。さらに、ジョブが終了以外のステータスを変更する場合(たとえば、ジョブが入力または出力のために停止した場合、または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"
    _

代替案

  1. フォアグラウンドプロセスグループ内の他のプロセスを変更して、SIGINTの間Childを無視します。これはあなたの質問には対応していませんが、あなたが望むものを手に入れるかもしれません。
  2. Childを次のように変更します:
    1. 現在の端末設定をバックアップするには、_stty -g_を使用します。
    2. _stty -isig_を実行して、INTRQUIT、およびSUSP文字を含む信号を生成しません。
    3. バックグラウンドで、端末入力を読み取り、必要に応じて自分で信号を送信します(例:_kill -QUIT 0Control+\ 読み取り、_kill -INT $$Control+C 読み込まれます)。これは些細なことではなく、Childスクリプトまたはそれが実行する何かがインタラクティブであることを意図している場合、これをスムーズに機能させることができない場合があります。
    4. 終了する前にターミナル設定を復元します(理想的にはEXITのトラップから)。
  3. _stty -isig_を実行するのではなく、#2と同じ、ユーザーがヒットするのを待つ Enter またはChildをkillする前の特別でないキー。
  4. setpgid()の呼び出しに使用できる独自のsetpgidユーティリティをC、Python、Perlなどで記述します。以下は粗雑な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
    _
11
Richard Hansen

POSIXから引用した章で説明されているように、SIGINTはフォアグラウンドプロセスグループ全体に送信されます。したがって、親プログラムの強制終了を回避するには、プログラムを独自のプロセスグループで実行するように調整します。

シェルは組み込みまたは構文の構文を介してsetpgrpへのアクセスを許可しませんが、シェルをインタラクティブに実行するという間接的な方法があります。 (トリックの StéphaneGimenez に感謝します。)

ksh -ic '
  … the part that needs to be interruptible without bothering the parent …
'

まあ、あなたがそれを参照したスタックオーバーフローの質問から、親が信号を処理するように構成する必要があることを明確に述べています。

第2に、POSIXリファレンスには、「ISIGが設定されている場合、INTR文字は処理時に破棄される」と明記されています。

これが2つのオプションです。 3つ目は、独自のプロセスグループで子を実行することです。

1
bahamat