Linux シグナルは、プロセスに特定のイベントの発生を通知したり、プロセスに特定の処理関数を実行させたりするために使用されます。シグナルは Unix ファミリの古い通信メカニズムです。シグナルはターミナルから送信されます。 Control-C によってトリガーされる SIGINIT などのキーボード文字入力は、無効なアドレスにアクセスするアプリケーションによってトリガーされる SIGSEGV、タイマーの期限切れによってトリガーされる SIGALARM など、ハードウェアまたはソフトウェアに関連する例外から発生することもあります。
#このチュートリアルの動作環境: linux5.9.8 システム、Dell G3 コンピューター。
Linux シグナルは何に使用されますか?
Linux のシグナル処理メカニズム
Signal は Unix ファミリの古い通信メカニズムで、主に特定のイベントの発生をプロセスに通知したり、イベントを許可したりするために使用されます。プロセスは特定の処理機能を実行します。 Unix システムの第一世代から存在しているため、古いものであると言われています。
信号は、Ctrl+C によってトリガーされる SIGINIT など、端末のキーボード文字入力から送信される場合があります。また、ハードウェアまたはソフトウェアから送信される場合もあります。無効なアドレスにアクセスするアプリケーションによって引き起こされる SIGSEGV (セグメンテーション違反)、タイマーの期限切れによって引き起こされる SIGALARM などの関連する異常。これらのシグナルはカーネルによってプロセスに送信されます。
プロセスによって受信されるシグナルは、他のプロセスからのものである場合もあります。ただし、すべてのプロセスが他のプロセスにシグナルを送信できるわけではありません。root 権限を持つスーパー ユーザーのみがこれを実行できます。通常のユーザー プロセスの場合、同じユーザーに属するプロセスにのみシグナルを送信できます。
プロセスはカーネルにシグナルを送信できますか?それは可能ですが、カーネル スレッドは応答せず、カーネル コードを変更しない限り役に立ちません。
通常、シグナルは非同期メカニズムとみなされますが、Linux コードでは、例外によって発生する次のシグナルも「同期」と呼ばれます:
#define SYNCHRONOUS_MASK \ (sigmask(SIGSEGV) | sigmask(SIGBUS) | sigmask(SIGILL) | \ sigmask(SIGTRAP) | sigmask(SIGFPE) | sigmask(SIGSYS))
ここではプロセスと呼びます。シグナルを受信するのは「ターゲットプロセス」です。従来のシグナル送信関数は kill() ですが、この名前は見た目が怖いし、対象のプロセスが「強制終了」されそうな感じがします。実際、すべてのシグナルは kill() を使用して送信されますが、実際に「強制終了」する必要があるのはプロセスのごく一部だけです。もちろん、現在は、より強力な機能とわかりやすい名前を備えた sigqueue() があります。
2 つの関数のプロトタイプを見てみましょう。
int kill(pid_t pid, int sig);int sigqueue(pid_t pid, int sig, const union sigval value);
pid は、ターゲット プロセスの PID を表します。 Linux のプロセスの PID はすべて正の数ですが、パラメータ pid の値が 0 または負の数の場合は不正ですか?いいえ、実際には、0 と負の数には他の用途があります。 Linux には、あるタイプのプロセスの集合を表すプロセス グループの概念があります。 kill() では、パラメータ pid が "0" の場合、現在のプロセスが存在するプロセス グループ内のすべてのプロセスにシグナルが送信されることを意味し、"-1" 未満の場合、シグナルは次のプロセスに送信されます。プロセス グループには -pid という番号が付けられます。
__kill_pgrp_info(sig, info, pid ? find_vpid(-pid) : task_pgrp(current));
pid が「-1」の場合は、Init プロセスとそれ自体を除くすべてのプロセス、またはそれ自体を除く pid が 1 より大きいすべてのプロセスに送信することを意味します。
for_each_process(p) { if (task_pid_vnr(p) > 1 && !same_thread_group(p, current)) ...
ここでの pid がプロセス自身の PID である場合、プロセスはそれ自体にシグナルを送信します。この目的のために、Linux は、より単純なインターフェイスである raise() 関数も提供します。
kill(getpid(), sig) --> raise(sig)
sigqueue() では、パラメーター pid の値を負の数に設定してプロセス グループ全体にシグナルを送信できないことに注意してください。
sig は送信するシグナルの番号を表します。Linux のシグナル番号は 1 から始まります。パラメーター sig の値が 0 の場合はどうなりますか?ここでの 0 にも魔法のような効果があり、sig が「0」の場合、実際には対象のプロセス (またはプロセス グループ) にシグナルを送信するのではなく、対象のプロセス (またはプロセス グループ) が存在するかどうかを検出するために使用されます。
sigqueue で追加された 3 番目のパラメータの定義は次のとおりです。
union sigval { int sival_int; void __user *sival_ptr;};
传统的信号是没有传递消息的功能的,sigval算是稍微扩展了一下信号的通信能力。比如,通信双方可以事先约定某些事件为特定的int值,这个"sival_int"就可以用来保存具体的int值,目标进程可以据此来区分不同的事件,做出不同的响应。当然,这种方法传递的消息内容受限,且不易扩展,因而不适合用作常规的通信手段。
就算是进程之间发送信号,那也是要经过内核的,可以理解成是被内核“截获”了吧。
由于内核态和用户态的切换操作在不同的硬件体系架构上是不同的,而且有一些信号,比如SIGCHLD和SIGSTOP,其实现也是和架构相关的,所以Linux中signal机制的很多代码都是放在架构相关的目录下的,比如"/arch/arm/kernel/signal.c"。
一个信号的相关信息在内核中用siginfo_t结构体表示:
siginfo_t{ int si_signo; int si_sicode; union __sifields _sifields; ...}
内核在截获到一个进程发送的信号后,会首先做一系列的检查,比如该信号的值是否合法啦,进程有没有发送这个信号的权限啦。如果检查通过,就调用copy_from_user()将该信号的相关信息复制到siginfo_t结构体中。
相关推荐:《Linux视频教程》
以上がLinux シグナルは何に使用されますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。