ctrl-Cで信号を無視するプログラムを強制的に終了できますか?

ctrl-Cで信号を無視するプログラムを強制的に終了できますか?

SIGINTを無視するプログラムがありますが、フォアグラウンドで実行したいと思います。 Ctrl-Cを押して強制的に閉じる方法を見つけたいです。./wrapper.sh my_program無視されたSIGINTを検出し、SIGKILLを生成して誤動作するプログラムを強制的に終了するようにラッパー(呼び出し可能)を作成する方法はありますか?

この回答私が探しているものとは正反対です。シグナルを無視するプログラムがSIGINTで終了するように強制したいと思います。

ベストアンサー1

SIGINTをSIGKILLに「変換」するラッパーを作成しましょう。

my_program+でSIGINTを実行してインポートするプロセスが必要です。 SIGINTを受け取ったら、SIGKILLを送信する必要があります。追加のプロセスのためにSIGKILLによって引き起こされたSIGINTを実際に受信する必要はありません。Ctrlcmy_programmy_programCtrlc

関連事実:

  • あるプロセスを別のプロセスで実行する標準的な方法は、プロセスの1つをバックグラウンドで非同期に実行することです(つまり、終了&)。
  • Ctrl+をクリックすると、cターミナルエミュレータはフォアグラウンドプロセスグループのプロセスにSIGINTを送信します。
  • 一部の(単純な)シェルは、同じプロセスグループですべてを実行します。バックグラウンドでコマンドを実行するように指示された場合、コマンドは入力を/dev/null盗むのを防ぐために標準入力を同等のファイルにリダイレクトします。
  • 他のシェルは、別々のプロセスグループで各コマンドを実行できます。バックグラウンドでコマンドを実行するように指示されたら、標準入力を変更せずに残します。バックグラウンドのコマンドは依然として制御端末から入力を盗むことはできません。SIGTTIN。これにより、シェルは端末に新しいフォアグラウンドプロセスグループを通知し、タスクをバックグラウンドからフォアグラウンドに移動できます。このメカニズムはジョブ制御と呼ばれ、無効にすることができます。スクリプトではデフォルトで無効になっています。
  • いずれにせよ、バックグラウンドプロセスは端末からデータを読み取ることができません。これは、stdinが端末であり、読み取る必要があるmy_program場合に備えてバックグラウンドで実行してはならないことを意味します。my_program
  • 残念ながら、一部のシェルではバックグラウンドで他のプロセスを実行しないようになっています。一部のシェルは、ジョブ制御が無効になっていても別のプロセスグループを使用します。フォアグラウンドプロセスグループに属していない他のプロセスはCtrl+からSIGINTを受信しないため、cこれをSIGKILLに「変換」することはできません。
  • 私のDebian 10には、posh同じプロセスグループのすべてを実行するシェルがあります。

その結果、次のラッパーが生成されます。

#!/usr/bin/env posh

( trap 'kill -s KILL 0' INT
  while kill -s 0 "$$" 2>/dev/null; do sleep 1; done
) &
exec "$@"

exec "$@"引数を使用して実行my_program(または指定したとおり)します。ありがとうございますexec my_program。包装紙を交換いたします。標準入力(おそらく端末)からデータを読み取ることができるだけでなく、そのPIDは置き換えられるラッパーの1つになります。この意味では、ラッパーは透明です。また、PIDは開始するmy_program前に知られておりmy_program、他のプロセス(この場合はバックグラウンドのサブシェル)で簡単にPIDを使用してmy_program実際に終了した時期を検出できるという優れた機能もあります。

"変換trap" SIGINTをSIGKILLに変換します。子プロセスがグループにある場合は、それらを含むプロセスグループ全体kill -s KILL 0にSIGKILLを送信することに注意してください(ただし、終了する場合は代わりにこれを使用してください)。ただし、その存在はテストのみ可能です。my_programmy_programkill -s KILL "$$"kill -s 0 "$$"my_program

同じプロセスグループですべてを実行するためにシェルを必要としない代替手段があります。秘密は、パイプラインのプロセスをプロセスグループで実行する必要があることです。賢いリダイレクトを使用すると、部分が相互に接続されていないパイプラインを構築できます。

#!/bin/sh -

exec 9>&1
( "$@"; kill -s TERM 0 ) >&9 9>&- | (
  trap 'kill -s KILL 0' INT
  while :; do sleep 1; done
)

このバリエーションではmy_programラッパーは交換されません。 2番目は、killSIGINTをSIGKILLに「変換」することです。 1つ目は、ループが生き残る状況でループが終了したkill場合にループを終了することです。my_program

期待どおりに動作しない可能性があるいくつかのシナリオがあります(ラッパーを使用)。その中には:

  • 分岐して終了すると、my_program実際の操作は子プロセスに委ねられます。問題の問題があるプログラムはCtrl+しようとするため、このように実行されない可能性が高いですc。しかし、一般的にそうです。
  • my_program子プロセスが別のプロセスグループで作成され、その親プロセスでそのプロセスを終了したい場合。
  • 端末が+からSIGINTを送信しmy_programないように設定されている場合。この問題が発生したと思われる場合は、それらの間に配置してラッパーを改善してください。この場合、プログラムはSIGINTを無視せずに端末からSIGINTをインポートしないようにすることもできます。したがって、SIGKILLは少し過剰になる可能性があります。この機能を端末に復元するだけで十分で、+が機能し始めます。Ctrlcstty -F /dev/tty intr ^Csleep 1doneCtrlc

コメントから:

SIGINTを無視するプログラムには通常、そのような理由があります。

本当。 SIGKILLの代わりにSIGTERMまたはSIGHUPを試してください。おそらく、問題のプログラムはそのうちの1つを無視せずに適切なクリーンアップを介して正常に終了する可能性があります。

おすすめ記事