Inter-process-communication-signals
プロセス間通信-シグナル
シグナルは番号または名前で指定できます。通常、シグナル名はSIGで始まります。 使用可能な信号は、コマンドkill –l(信号名をリストするためのl)で確認できます。これは次のとおりです-
シグナルが発生するたび(プログラムまたはシステムで生成されたシグナル)、デフォルトのアクションが実行されます。 デフォルトのアクションを実行したくないが、信号の受信時に独自のアクションを実行したい場合はどうなりますか? これはすべての信号で可能ですか? はい、信号を処理することはできますが、すべての信号を処理することはできません。 信号を無視したい場合、これは可能ですか? はい、信号を無視することは可能です。 シグナルを無視することは、デフォルトのアクションの実行もシグナルの処理も行わないことを意味します。 ほとんどすべての信号を無視または処理できます。 無視または処理/捕捉できないシグナルは、SIGSTOPおよびSIGKILLです。
要約すると、信号に対して実行されるアクションは次のとおりです-
- デフォルトのアクション
- 信号を処理する
- 信号を無視する
説明したように、シグナルはデフォルトアクションの実行を変更して処理できます。 シグナル処理は、システムコール、signal()およびsigaction()の2つの方法のいずれかで実行できます。
システムコールsignal()は、signumで述べられているように、シグナルの生成時に登録済みハンドラーを呼び出します。 ハンドラーは、SIG_IGN(シグナルの無視)、SIG_DFL(シグナルをデフォルトのメカニズムに戻す)、またはユーザー定義のシグナルハンドラーまたは関数アドレスのいずれかです。
このシステムコールは、成功時に整数引数を取り、戻り値を持たない関数のアドレスを返します。 この呼び出しは、エラーの場合にSIG_ERRを返します。
signal()を使用すると、ユーザーが登録した各シグナルハンドラーを呼び出すことができますが、ブロックする必要のあるシグナルのマスキング、シグナルの動作の変更、その他の機能などの微調整はできません。 これは、sigaction()システムコールを使用して可能です。
このシステムコールは、シグナルアクションを検査または変更するために使用されます。 行為がnullでない場合、信号signumの新しい行為が行為からインストールされます。 oldactがnullでない場合、以前のアクションはoldactに保存されます。
sigaction構造には次のフィールドが含まれています-
- フィールド1 *-sa_handlerまたはsa_sigactionで言及されたハンドラー。
sa_handlerのハンドラーは、signumに基づいて実行されるアクションを指定し、SIG_DFLはデフォルトのアクションを示し、SIG_IGNはシグナルまたはシグナル処理関数へのポインターを無視します。
sa_sigactionのハンドラーは、最初の引数としてシグナル番号、2番目の引数としてsiginfo_t構造体へのポインター、3番目の引数としてユーザーコンテキストへのポインター(詳細についてはgetcontext()またはsetcontext()を確認)を指定します。
構造体siginfo_tには、配信されるシグナル番号、シグナル値、プロセスID、送信プロセスの実際のユーザーIDなどのシグナル情報が含まれます。
- フィールド2 *-ブロックする信号のセット。
この変数は、シグナルハンドラーの実行中にブロックされるべきシグナルのマスクを指定します。
- フィールド3 *-特別なフラグ。
このフィールドは、信号の動作を変更するフラグのセットを指定します。
- フィールド4 *-ハンドラーを復元します。
このシステムコールは、成功すると0を返し、失敗すると-1を返します。
いくつかのサンプルプログラムを考えてみましょう。
まず、例外を生成するサンプルプログラムから始めましょう。 このプログラムでは、システムが例外を生成するゼロ除算を実行しようとしています。
コンパイルおよび実行手順
したがって、算術演算を実行しようとすると、システムはコアダンプを伴う浮動小数点例外を生成します。これは、信号のデフォルトアクションです。
次に、signal()システムコールを使用してこの特定の信号を処理するようにコードを変更しましょう。
コンパイルおよび実行手順
説明したように、信号はシステムによって生成され(ゼロ除算などの特定の操作を実行すると)、ユーザーはプログラムで信号を生成することもできます。 プログラムで信号を生成する場合は、ライブラリ関数raise()を使用します。
次に、信号の処理と無視を示す別のプログラムを取り上げます。
raise()を使用してシグナルを発生させたと仮定すると、その後はどうなりますか? シグナルを上げた後、現在のプロセスの実行が停止します。 次に、停止したプロセスはどうなりますか? 2つのシナリオがあります。まず、必要なときに実行を継続します。 次に、プロセスを(killコマンドで)終了します。
停止したプロセスの実行を継続するには、SIGCONTをその特定のプロセスに送信します。 fg(フォアグラウンド)またはbg(バックグラウンド)コマンドを発行して、実行を継続することもできます。 ここでは、コマンドは最後のプロセスの実行のみを再開します。 複数のプロセスが停止した場合、最後のプロセスのみが再開されます。 以前に停止したプロセスを再開する場合は、ジョブ番号とともにジョブを(fg/bgを使用して)再開します。
次のプログラムは、raise()関数を使用してシグナルSIGSTOPを発生させるために使用されます。 シグナルSIGSTOPは、CTRL + Z(Control + Z)キーを押すことでも生成できます。 このシグナルを発行した後、プログラムは実行を停止します。 シグナル(SIGCONT)を送信して、実行を継続します。
次の例では、コマンドfgで停止したプロセスを再開しています。
コンパイルおよび実行手順
ここで、別の端末からSIGCONTを発行して、停止したプロセスの実行を継続するように前のプログラムを強化します。
コンパイルおよび実行手順
別のターミナルで
これまで、システムによって生成された信号を処理するプログラムを見てきました。 次に、プログラムで生成されたシグナルを見てみましょう(raise()関数またはkillコマンドを使用)。 このプログラムは、シグナルSIGTSTP(端末停止)を生成します。デフォルトのアクションは、実行を停止することです。 ただし、現在はデフォルトのアクションではなくシグナルを処理しているため、定義されたハンドラーに到達します。 この場合、メッセージを出力して終了するだけです。
コンパイルおよび実行手順
デフォルトのアクションを実行したり、シグナルを処理したりするインスタンスを見てきました。 今、信号を無視する時です。 ここで、このサンプルプログラムでは、SIG_IGNを介して無視するシグナルSIGTSTPを登録し、シグナルSIGTSTP(ターミナルストップ)を上げています。 無視されるシグナルSIGTSTPが生成されているとき。
コンパイルおよび実行手順
これまでのところ、1つのシグナルを処理するシグナルハンドラーが1つあることを確認しました。 単一のハンドラーで複数の信号を処理できますか? 答えはイエスです。 これをプログラムで考えてみましょう。
次のプログラムは次のことを行います-
- ステップ1 *-シグナルSIGINT(CTRL + C)またはSIGQUIT(CTRL + \)をキャッチまたは処理するハンドラー(handleSignals)を登録します。
- ステップ2 *-ユーザーがシグナルSIGQUITを生成する場合(killコマンドまたはCTRL + \を使用したキーボード制御のいずれかを介して)、ハンドラーは単に戻り値としてメッセージを出力します。
- ステップ3 *-ユーザーがシグナルSIGINT(killコマンドまたはCTRL + Cを使用したキーボード制御)を初めて生成する場合、次回からデフォルトアクション(SIG_DFLを使用)を実行するようにシグナルを変更します。
- ステップ4 *-ユーザーが2回目にシグナルSIGINTを生成すると、デフォルトのアクション(プログラムの終了)が実行されます。
コンパイルおよび実行手順
別のターミナル
第二の方法
シグナルを処理するには、signal()またはsigaction()の2つのシステムコールがあることを知っています。 これまでsignal()システムコールで見てきましたが、今はsigaction()システムコールの時間です。 次のようにsigaction()を使用して実行するように上記のプログラムを変更しましょう-
コンパイルと実行のプロセスを見てみましょう。 実行プロセスで、CTRL + Cを2回発行し、残りのチェック/方法(上記)を確認して、このプログラムでも試してみます。