SIGINTが親プロセスに伝播するのを防ぎます

SIGINTが親プロセスに伝播するのを防ぎます

親プログラム(C ++プログラムまたはシェルスクリプトのいずれか)がサブシェルスクリプトを実行するシナリオを考えてみましょう。サブシェルスクリプトの実行中にCtrl + C(またはINTR文字で構成される文字)を押すと、SIGINTは次のようになります。のすべてのプロセスをフォアグラウンドプロセスグループに送信します。これには親プロセスが含まれます。

源泉:POSIX.1-2008 XBDセクション11.1.9

このデフォルトの動作をオーバーライドする方法はありますか?子プロセスはシグナルを親プロセスに伝播せずに単独で処理しますか?

引用:スタックオーバーフローポスト - 子プロセスが中断されると親プロセスは完了しません(TRAP INT)

答え1

(Gilesの答えからインスピレーションを得た)

フラグが設定されている場合、スクリプトが親プロセスをインポートせずにインポートする唯一の方法は、ISIG独自のプロセスグループにスクリプトを配置することです。これはオプションで行うことができます。ChildSIGINTSIGINTset -m

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

ここにいる-mPOSIX オプションの説明:

-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"
    

代替アイデア

  1. SIGINT期間中に無視されるように、フォアグラウンドプロセスグループの他のプロセスを変更しますChild。これで問題は解決しませんが、欲しいものを手に入れることができます。
  2. に変更Child:
    1. stty -g現在の端末設定をバックアップするために使用されます。
    2. 、および文字を含む信号は実行stty -isig時に生成されません。INTRQUITSUSP
    3. バックグラウンドで端末入力を読み取り、必要に応じて信号自体を送信します(例:kill -QUIT 0読み取り+で実行、読み取り+で実行)。これは些細な問題ではなく、スクリプトや実行中のすべての項目が対話型である場合、スムーズに機能しない可能性があります。Control\kill -INT $$ControlCChild
    4. 終了する前に端末設定を復元してください(好ましくは上記EXIT)。
  3. を実行する代わりに、stty -isigユーザーが殺す前Enterに押すか、他の非特殊キーを押すのを待つことを除いて、#2と同じですChild
  4. setpgidsetpgid()以下はおおよその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番目の方法は、独自のプロセスグループで子プロセスを実行することです。

関連情報