シグナル
シグナルはそれを識別する番号で、1から31までを、標準シグナル、32から64をリアルタイムシグナルとして利用するようです。標準シグナルは1つのシグナルしか受け付けませんが、リアルタイムシグナルはすべて受け付けます。実装的に言うと、標準シグナルは受付済みのシグナルを受け取ると、それを無視します。リアルタイムシグナルはそれを待ち行列に登録すると言う具合です。
シグナルはプロセスに送るメッセージのような物ですが、ここでの送るという表現で、そのやり取りになにやら通信的なイメージを想像しますが、実際は送信元プロセスがシグナルを生成し、それを送信先のシグナルウエイトキューに登録することで実現しています。従ってシグナル処理では、作成と配信の2つ処理があるわけですが、ここでは作成について見てみました。
killコマンドをシグナルを送信するように、シグナルを送信する方法はいくつかあります。しかしこれらは最終的には send_signal関数が呼ばれて、シグナル作成の処理が行われます。
send_signal関数では、まずprepare_signal関数で前チェックが行われます。これは送信しようとしているプロセスが終了状態か? シグナルが、STOPの場合、送信先のプロセスがSIGCONTを有しているなら、それを取り除きます。また反対に、シグナルがSIGCONTの場合、送信先のプロセスからSIGSTOPを取り除いたりいたします。
次にlegacy_queue関数では、シグナルが通常シグナルかリアルタイムシグナルかチェックします。通常シグナルでしかもそれが既に保留しているなら、send_signal関数はその処理をしないでreturnしています。
引数のinfoはシグナル種別で、このマクロから見る限り0,1,2の値をとるようで、send_signal関数を呼ぶ上位のシグナル関数によって指定されるものです。
#define SEND_SIG_NOINFO ((struct siginfo *) 0)
#define SEND_SIG_PRIV ((struct siginfo *) 1)
#define SEND_SIG_FORCED ((struct siginfo *) 2)
if (info == SEND_SIG_FORCED)の場合、ウエイトキューにリストする処理をスキップして、直接そのシグナルを有するプロセスをwake_upする処理となります。そうでない場合、__sigqueue_alloc関数でsigqueue pを取得し、それにシグナル情報を設定していきます。設定内容は、SEND_SIG_NOINFO、SEND_SIG_PRIVそれ以外で、内容が違っていますが、この辺りは配信での処理とのかかわりだと思います・・・。
このsigqueue *qを、list_add_tail(&q->list, &pending->list)でpending->listに追加しています。pending->listはpending = group ? &t->signal->shared_pending : &t->pendingで、送信先のプロセスヂスクリプタに紐づくメンバーです。
p/s
infoはSEND_SIG_NOINFO、SEND_SIG_PRIV、SEND_SIG_FORCEDの値と、info構造体そのもを渡す場合があるようですが、どのようなケースなのか、またその意味するところは分かりません。
なお、グループとして送信するものをt->signal->shared_pendingに、そのプロセスだけに送信するものをt->pendingに別々に登録しているようです。
そして、該当するプロセスを起床させます。まずsignalfd_notify関数です。この関数はtsk->sighand->signalfd_wqhにリストされているプロセスを起床させます。こあれはsignalfdシステムコールでシグナルを受信するリストです。
sigaddset関数はpending->signal->sig[0] |= 1UL << sigで、どのシグナルをリストしているのビット情報を設定し、最後にcomplete_signal関数が、該当のシグナルを保留しているプロセスを起床させています。
シグナルはプロセスに送るメッセージのような物ですが、ここでの送るという表現で、そのやり取りになにやら通信的なイメージを想像しますが、実際は送信元プロセスがシグナルを生成し、それを送信先のシグナルウエイトキューに登録することで実現しています。従ってシグナル処理では、作成と配信の2つ処理があるわけですが、ここでは作成について見てみました。
killコマンドをシグナルを送信するように、シグナルを送信する方法はいくつかあります。しかしこれらは最終的には send_signal関数が呼ばれて、シグナル作成の処理が行われます。
send_signal関数では、まずprepare_signal関数で前チェックが行われます。これは送信しようとしているプロセスが終了状態か? シグナルが、STOPの場合、送信先のプロセスがSIGCONTを有しているなら、それを取り除きます。また反対に、シグナルがSIGCONTの場合、送信先のプロセスからSIGSTOPを取り除いたりいたします。
次にlegacy_queue関数では、シグナルが通常シグナルかリアルタイムシグナルかチェックします。通常シグナルでしかもそれが既に保留しているなら、send_signal関数はその処理をしないでreturnしています。
引数のinfoはシグナル種別で、このマクロから見る限り0,1,2の値をとるようで、send_signal関数を呼ぶ上位のシグナル関数によって指定されるものです。
#define SEND_SIG_NOINFO ((struct siginfo *) 0)
#define SEND_SIG_PRIV ((struct siginfo *) 1)
#define SEND_SIG_FORCED ((struct siginfo *) 2)
if (info == SEND_SIG_FORCED)の場合、ウエイトキューにリストする処理をスキップして、直接そのシグナルを有するプロセスをwake_upする処理となります。そうでない場合、__sigqueue_alloc関数でsigqueue pを取得し、それにシグナル情報を設定していきます。設定内容は、SEND_SIG_NOINFO、SEND_SIG_PRIVそれ以外で、内容が違っていますが、この辺りは配信での処理とのかかわりだと思います・・・。
このsigqueue *qを、list_add_tail(&q->list, &pending->list)でpending->listに追加しています。pending->listはpending = group ? &t->signal->shared_pending : &t->pendingで、送信先のプロセスヂスクリプタに紐づくメンバーです。
p/s
infoはSEND_SIG_NOINFO、SEND_SIG_PRIV、SEND_SIG_FORCEDの値と、info構造体そのもを渡す場合があるようですが、どのようなケースなのか、またその意味するところは分かりません。
なお、グループとして送信するものをt->signal->shared_pendingに、そのプロセスだけに送信するものをt->pendingに別々に登録しているようです。
そして、該当するプロセスを起床させます。まずsignalfd_notify関数です。この関数はtsk->sighand->signalfd_wqhにリストされているプロセスを起床させます。こあれはsignalfdシステムコールでシグナルを受信するリストです。
sigaddset関数はpending->signal->sig[0] |= 1UL << sigで、どのシグナルをリストしているのビット情報を設定し、最後にcomplete_signal関数が、該当のシグナルを保留しているプロセスを起床させています。
kernel/signal.c static int send_signal(int sig, struct siginfo *info, struct task_struct *t, int group) { struct sigpending *pending; struct sigqueue *q; assert_spin_locked(&t->sighand->siglock); if (!prepare_signal(sig, t)) return 0; pending = group ? &t->signal->shared_pending : &t->pending; if (legacy_queue(pending, sig)) return 0; if (info == SEND_SIG_FORCED) goto out_set; q = __sigqueue_alloc(t, GFP_ATOMIC, (sig < SIGRTMIN && (is_si_special(info) || info->si_code >= 0))); if (q) { list_add_tail(&q->list, &pending->list); switch ((unsigned long) info) { case (unsigned long) SEND_SIG_NOINFO: q->info.si_signo = sig; q->info.si_errno = 0; q->info.si_code = SI_USER; q->info.si_pid = task_pid_vnr(current); q->info.si_uid = current->uid; break; case (unsigned long) SEND_SIG_PRIV: q->info.si_signo = sig; q->info.si_errno = 0; q->info.si_code = SI_KERNEL; q->info.si_pid = 0; q->info.si_uid = 0; break; default: copy_siginfo(&q->info, info); break; } } else if (!is_si_special(info)) { if (sig >= SIGRTMIN && info->si_code != SI_USER) return -EAGAIN; } out_set: signalfd_notify(t, sig); sigaddset(&pending->signal, sig); complete_signal(sig, t, group); return 0; }complete_signal関数では再度シグナル配信を行うかのチェックとか、そのタスクが必要なくてもグループスレッドに配信する必要があるかどうかのチェックを行った後、signal_wake_up関数で該当のプロセスを起床させます。ここで味噌となるのが、set_tsk_thread_flag(t, TIF_SIGPENDING)です。これはプロセス構造体のtsk->thread.infoに、SIGPENDINGをセットしていることです。シグナルの配信のためです。システムコールのゆに、カーネルはカーネルモードからユーザモードに戻る際、このフラグをチェックし、シグナルを配信処理をする必要があるかどうかチェックをするようにしています。
void signal_wake_up(struct task_struct *t, int resume) { unsigned int mask; set_tsk_thread_flag(t, TIF_SIGPENDING); mask = TASK_INTERRUPTIBLE; if (resume) mask |= TASK_WAKEKILL; if (!wake_up_state(t, mask)) kick_process(t); }実際プロセスを起床させるのは、wake_up_state関数です。なおこの関数で起床できなかったということは、該当するプロセスは他のCPUで動作しているということで、kick_process関数でそのプロセスの動作しているCPUでの起床処理を行うようになっているようです。(たぶん)