プロセス信号 2
1. 信号の保存
1. 信号に関連するその他の共通概念
- 信号処理アクションの実際の実行は、信号配信 (Deliver)と呼ばれます。
- シグナルの生成と配信の間の状態をシグナル保留 (Pending)と呼びます。
プロセスはシグナルをブロック(Block)することを選択できます。ブロックされたシグナルが生成されると、プロセスが配信アクションを実行する前にシグナルのブロックを解除するまで保留状態のままになります。
注:ブロックと無視は異なります。信号がブロックされている限り配信されませんが、無視は配信後のオプションの処理アクションです。
2. 信号はカーネルで表現されます
カーネル内の信号の概略図:
オペレーティング システム カーネルには 3 つのテーブルがあり、2 つはビットマップ構造 (block
とpending
)、1 つは関数ポインター配列構造です。
-
block
表: ビットマップ構造内の対応する位置のビットが であるかどうかは1
、信号がブロックされているかどうかを表します。 -
pending
表: ビットマップ構造内の対応する位置にあるビットが であるかどうかは1
、信号が保留中かどうかを表します。 -
handler
テーブル: テーブルには関数ポインターが格納され、対応する添え字内の関数ポインターは、シグナルの受信時に呼び出す関数を示します。
(3 つのテーブル全体で、データは論理的に水平方向に転送されます)
中のシグナルはSIG_DFL
デフォルトアクションの実行を意味し、SIG_IGN
無視アクションの実行を意味します。それらは次のように定義されます。
以前に使用した関数の原理は、テーブル内の対応する位置にある関数ポインターsignal
を変更することです。handler
説明:
-
各シグナルには、ブロッキング (block) と保留 (pending)を示す 2 つのフラグと、処理アクションを示す関数ポインターがあります。シグナルが生成されると、カーネルはプロセス制御ブロック内のシグナルの保留フラグを設定し、シグナルが配信されるまでフラグはクリアされません。上の例では、
SIGHUP
シグナルはブロックも生成もされず、配信時にデフォルトの処理アクションが実行されます。 -
SIGINT
信号は生成されていますがブロックされているため、一時的に配信できません。プロセスの処理アクションは無視されますが、ブロックを解除する前にプロセスには処理アクションを変更する機会があるため、ブロックを解除する前にこの信号を無視することはできません。 -
SIGQUIT
シグナルは生成されておらず、SIGQUIT
シグナルが生成されるとブロックされ、その処理アクションはユーザー定義関数になりますsighandler
。
プロセスがブロックを解除する前にシグナルが複数回生成された場合の対処方法?
POSIX.1 では、システムがこの信号を 1 回以上配信することができます。Linux は次のように実装されています。通常のシグナルは配信前に複数回生成され、1 回だけカウントされますが、リアルタイム シグナルは配信前に複数回生成され、順番にキューに入れられます。
3. システムカーネルに設定される信号
カーネル内の信号の概略図から: 各信号には、0 または 1 のいずれかである 1 ビットの保留フラグのみがあり、信号が生成された回数は記録されません。また、ブロッキング フラグもこの図で表されます。道。
したがって、保留フラグとブロックされたフラグは、シグナル セットと呼ばれる同じデータ型で保存できます。この型の本質は、各シグナルの「有効」または「無効」ステータスを表すことができるビットマップ構造ですsigset_t
。sigset_t
信号セット内の「アクティブ」および「非アクティブ」の意味は、信号がブロックされているかどうかを示します。一方、保留信号セット内の「アクティブ」および「非アクティブ」の意味は、信号が保留中かどうかを示します。ブロック信号セットは、現在のプロセスの信号マスク (信号マスク) とも呼ばれます。ここでの「シールド」は、無視するのではなくブロックすることとして理解する必要があります。
sigset_t タイプの概要
sigset_t
これはLinux
オペレーティング システムによって提供されるデータ型です。その基礎となるパッケージはlong
型の配列です。この配列の各ビットを使用して関連情報を表します。
2. 信号セット操作機能
1. sigset_t型の演算関数
sigset_t
このタイプでは、信号ごとにビットを使用して「有効」または「無効」ステータスを示します。これらのビットをこのタイプ内にどのように格納するかについては、システムの実装に依存します。ユーザーの観点からは、それを使用する必要はありません。ユーザーは、sigset_ t
変数の内部データを直接操作する代わりに、次の関数を呼び出して変数を操作することしかできません。
-
この関数は、
sigemptyset
set が指す信号セットを初期化し、その中のすべての信号の対応するビットをクリアして、信号セットに有効な信号が含まれていないことを示します。 -
この関数は、
sigfillset
set で指定された信号セットを初期化し、その中のすべての信号の対応するビットを設定します。これは、1
この信号セットの有効な信号にシステムがサポートするすべての信号が含まれていることを示します。 -
この機能は、
sigaddset
set が指す信号セット内の信号signum
に対応するビット位置を設定することです。1
-
この機能は、
sigdelset
set が指す信号セット内の信号signum
に対応するビット位置を設定することです。0
-
sigismember
信号セットにsignum信号が含まれているかどうかを判定するブール関数で、含まれている場合は1を返し、含まれていない場合は0を返し、失敗した場合は-1を返します。 -
sigset_ t
この型の変数を使用する前に、信号を確定状態に設定するために変数を呼び出すsigemptyset
か初期化する必要があることに注意してください。sigfillset
変数が初期化された後、sigset_t
呼び出しsigaddset
および信号セットにsigdelset
有効な信号を追加または削除することができます。 -
これら 4 つの関数はすべて正常に戻り
0
、エラー時にも戻ります。-1
2. ブロックテーブルに関するシステムコール
sigprocmask
呼び出し元の関数は、プロセスのシグナル マスク (ブロッキング シグナル セット) を読み取りまたは変更できます。
-
パラメータ:
-
最初のパラメータはフラグ ビットで、次の 3 つのオプションから選択できます:
(現在の信号マスクがマスクであると仮定します)。
パラメータ | 関数 |
---|---|
SIG_BLOCK | set には、現在の信号マスクに追加する信号が含まれており、これはマスク = マスク | セットと同等です。 |
SIG_ブロック解除 | set には、現在の信号マスクからブロックを解除したい信号が含まれます。mask=mask&~set と同等です。 |
SIG_SETMASK | 現在の信号マスク ワードを set が指す値に設定します。これは、mask=set と同等です。 |
-
2 番目のパラメータは信号セットです。
-
3 番目の信号は出力パラメータであり、システムが
block
信号セットに新しい信号セットを設定すると、古い信号セットの内容が抽出されてコピーされますoldset
。 -
戻り値: 呼び出しが成功した場合は戻り
0
、呼び出しが失敗した場合は戻り-1
、エラー コードが設定されます。
ブロックされた信号の場合、ブロックが解除されるとすぐに配信されるため、コールが現在保留中のsigprocmask
いくつかの信号のブロックを解除すると、sigprocmask
戻る前に少なくとも 1 つの信号が配信されます。
以下では、上記の理論を証明するためにコード デモンストレーションを使用します。次のコードは、最初に2
数値現在のプロセスのマスク入力時にキーボードを押して、Ctrl + Cプロセスが終了するかどうかを確認します。終了しない場合は、現在のシグナルが実際にブロックされていることを意味します。最後の 5 秒後に、元のシグナルが復元されます。 No. 2 はすぐに配信され、プロセスは終了します。
#include <iostream>
#include <signal.h>
#include <unistd.h>
// 打印sigset_t结构
void Printset(sigset_t* set)
{
for (int i = 1; i <= 31;i++)
{
if (sigismember(set, i))
{
std::cout << "1";
}
else
{
std::cout << "0";
}
}
}
int main()
{
// 变量的初始化
int count = 0;
sigset_t set, oset;
sigemptyset(&set);
sigemptyset(&oset);
// 设置当前进程的信号屏蔽字
sigaddset(&set, SIGINT);
sigprocmask(SIG_SETMASK, &set, &oset);
// 打印出老的信号屏蔽字
std::cout << "老的信号屏蔽字是:";
Printset(&oset);
std::cout << std::endl;
while (true)
{
std::cout << "目前信号屏蔽字是:";
Printset(&set);
std::cout << std::endl;
if (count++ == 5)
{
// 恢复原来的信号屏蔽字
std::cout << "恢复原来的信号屏蔽字" << std::endl;
sigprocmask(SIG_SETMASK, &oset, &set);
}
sleep(1);
}
return 0;
}
実行結果を図に示します。結果は期待どおりです。
3. 保留テーブルに関するシステムコール
保留中のテーブルを変更することはできません。システム コールを通じてのみ表示できます。
sigpending
この関数は非常に単純です。この関数は、現在のプロセスの保留中のシグナル セットを読み取り、設定されたパラメーターを通じて送信します。呼び出しが成功した場合は 0 を返し、失敗した場合は -1 を返します。
2
上記の原則を検証し続けるために例を書いてみましょう: 最初に数値シグナルをシールドし、次に現在のプロセスの保留中のシグナルセットを出力します。最初は、プロセスがシグナルを受信しなかったため、すべて 0 が表示されるはずです。次に、2
プロセスに数値シグナルを送信します。数値 2 のシグナルがブロックされているため、現在のプロセスの保留中のシグナル セットは、2 番目のビットが 1 で、その他はすべて 0 である必要があります。
#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>
// 打印sigset_t结构
void Printset(sigset_t* set)
{
for (int i = 1; i <= 31;i++)
{
if (sigismember(set, i))
{
std::cout << "1";
}
else
{
std::cout << "0";
}
}
}
int main()
{
// 变量的初始化
int count = 0;
sigset_t set, oset, pending;
sigemptyset(&set);
sigemptyset(&oset);
// 设置当前进程的信号屏蔽字
sigaddset(&set, SIGINT);
sigprocmask(SIG_SETMASK, &set, &oset);
while (true)
{
// 打印未决信号集
sigpending(&pending);
std::cout << "目前的未决信号集是:";
Printset(&pending);
std::cout << std::endl;
if (count++ == 5)
{
// 恢复原来的信号屏蔽字
std::cout << "恢复原来的信号屏蔽字" << std::endl;
sigprocmask(SIG_SETMASK, &oset, &set);
}
sleep(1);
}
return 0;
}
3. 結論
この章ではプロセス信号の保存について説明します。信号保存の概念はあまりにも大きく、実際の動作は比較的小さいため、信号保存のためのオペレーティング システム カーネル内のblock
pending
handler
テーブル
次の章では、プロセス信号の処理をさらに詳しく理解し、信号についての理解をさらに深めていきます。もちろん、この記事に間違いや不足がある場合は、コメントまたはプライベートメッセージで議論してください。また次回、バイバイ!