Linux カーネルシグナルの実装と使用
以前書いたことのいくつかを投稿して、みんなで学びましょう。
1. 基本的なデータ構造
* Linux シグナル番号の構造
下の図は、『Linux カーネルの詳細 第 3 版』の「シグナル」の章の図です
{task_struct}[...][シグナル]---------------------------------[サインハンド][ブロックされました][real_balocked ][saved_sigmask][pending][notifier][notifier_mask][...]
* 信号処理データ構造体 struct sigaction { __sighandler_t sa_handler; //信号処理関数ポインタ unsigned long sa_flags; bit option_ _sigrestore_t sa_restorer; /* 拡張性のためのマスク last */ //each};struct k_sigaction { struct sigaction sa;};
* シグナル ハンドラーのタイプ */typedef void ( *__sighandler_t )(int);
* 信号の値を保存する構造は、マシンの CPU のビット数に基づきます: 32 ビットの場合、2 つの長整数配列 (合計 64 ビット)。必要; 64 ビットの場合、必要な長整数配列は 1 つだけです。 整数の配列 (これも 64 ビット)。 / _NSIG_BPW)typedef unsigned long old_sigset_t; /* 少なくとも 32 ビット */ //信号値を保存するビット配列。各ビットは信号値を表します。 typedef struct { unsigned long sig[_NSIG_WORDS] //ビット数の定義に従った整数信号} sigset_t;
* プロセス記述子内の信号処理構造体 struct sighand_struct { atomic_t count; struct k_sigaction action[_NSIG] //各信号値は k_sigaction 構造体に対応; //シグナルスピンロック wait_queue_head_t signalfd_wqh; //シグナル待機キュー}; ret; // シグナル処理関数を設定します new_sa.sa.sa_handler = handler; // sig 後にシグナルをクリアし、現在のシグナルがマスクされるのを防ぎます。 // 最初に new_sa の信号ビットをすべてクリアします sigemptyset(&new_sa.sa.sa_mask); // Ret = do_sigaction(sig, &new_sa, &old_sa); return ret : (unsigned long)old_sa.sa.sa_handler;}
。信号処理関数のインストール int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact){ // 現在のプロセス記述子のアドレスを取得します。つまり、現在の struct task_struct *t = current; のアドレスを直接取得します。 k; sigset_t Mask; // 信号の有効性をチェックします: // (1) sig が 64 より大きいか 1 より小さい場合、パラメータ値エラーを返します // (2) 信号処理構造体が空でない場合、および sig SIGKILL または SIGSTOP の場合、パラメータ エラーが返されます。KILL と STOP はカーネルによって処理され、ブロックできないためです。 if (!valid_signal(sig) | | sig
sighand->action[sig-1]; // スピン ロックを使用してロックpin_lock_irq(¤t->signand->siglock); // 現在のプロセスの信号処理構造体を一時変数 oact に割り当て、それを保存します if (oact) *oact = *k; // 設定される信号構造体がNULL if (act) { // マスクされた信号フィールドから SIGKILL および SIGSTOP 信号を削除します。これらの 2 つの信号はマスク可能ではありません。 sigdelsetmask( &act->sa.sa_mask, sigmask(SIGKILL) | sigmask(SIGSTOP));新しいシグナル処理構造体を使用した現在のプロセスのプロセス処理構造体 (シグハンド) *k = *act; /* * POSIX 3.3 .1.3: * "保留中のシグナルに対してシグナル アクションを SIG_IGN に設定すると、 * 保留中のシグナルが発生します。 * ブロックされているかどうかに関係なく、シグナルは破棄されます。" * * "保留中であり、デフォルトのアクションがシグナルを無視することである * (SIGCHLD など) シグナルのシグナル アクションを SIG_DFL に設定すると、 */ // (1) 待機中のシグナルを処理する場合 SIG_IGN に関数を設定すると、シグナルがブロックされているかどうかに関係なく、シグナルが失われます。 // (2) 待機状態にあり、デフォルトの処理メソッドが無視されるシグナル (SIGCHLD など) の処理関数が SIG_DFL に設定されている場合、シグナルがブロックされているかどうかに関係なく、待機中のシグナルが失われる可能性があります。
// (1) 信号処理構造体が SIG_IGN に設定されている場合、直接 TRUE が返されます。 // (2) 処理関数が SIG_DFL に設定されており、sig がカーネルによって無視されるシグナルの場合は、直接 TRUE が返されます。// 信号処理関数が SIG_IGN に設定されている場合、プロセス記述子のshared_pending 共有信号キューからこれらの信号を削除します。 if (sig_handler_ignored(sig_handler(t, sig), sig)) { // マスク信号ビット構造体をクリア sigemptyset(&mask) // 信号値に従って対応する信号ビットを設定 sigaddset(&mask, sig); // 変更sig シグナルはプロセス記述子のブロッキングシグナルキューから削除されます。 rm_from_queue_full(&mask, &t->signal->shared_pending); // 現在のプロセス記述子の保留キューから sig シグナルを削除します。 do { rm_from_queue_full(&mask, &t->pending); // プロセスのスレッドに対して同じことを行います。 t = next_thread(t); } while (t != current); } spin_unlock_irq(¤t->sighand->siglock); 信号情報構造体 typedef struct siginfo { //Signal id (id は 32 ~ 64) および非リアルタイム信号 (id は 0 ~ 32) を含む int si_errno; // Union { int _pad[SI_PAD_SIZE]; /* kill() */ struct { pid_t _pid; /* 送信者の pid */ __ARCH_SI_UID_T _uid; /* POSIX.1b タイマー */ struct { timer_t _tid; /* オーバーラン; count */ char _pad [sizeof( __ARCH_SI_UID_T) - sizeof(int)]; /* 以下と同じ */ int _sys_private; /* ユーザーには渡されません */ } _timer;
/* POSIX.1b シグナル*/ struct { pid_t _pid; /* 送信者の pid */ __ARCH_SI_UID_T _uid; /* SIGCHLD */ struct { pid_t _pid; /* 送信者のuid */ int _status ; /* 終了コード */ Clock_t _utime; Clock_t _stime } /* SIGILL、SIGSEGV、SIGBUS */ struct { void __user *_addr; /* 障害のある insn/memory ref。 ifdef __ARCH_SI_TRAPNO int _trapno; /* シグナルを発生させた TRAP # */#endif } _sigfault; /* SIGPOLL */ struct { __ARCH_SI_BAND_T _band; /* int _fd; } _sifields; t ;
* シグナル送信システムコールの関数呼び出し関係は以下の通り: sys_kill() -> kill_something_info() -> if(pid>0) kill_pid_info() -> if(pid!=-1) __kill_pgrp_info() - > else group_send_sig_info()
シグナル送信はシステムコールlong kill(pid_t pid, int sig);を通じて行われます。次に、このシステム コールの実装を見てみましょう。 asmlinkage longsys_kill(pid_t pid, int sig){ struct siginfo info; // シグナル値を設定します info.si_signo = sig; // エラー番号を 0 に設定します。 0; // シグナル送信フラグを設定します。0 は、kill や raise などのシステム コールによって送信されることを示します。整数は、カーネルによって送信されることを示します。 info.si_code = SI_USER; // シグナル プロセス番号情報を設定します。 si_pid = task_tgid_vnr(current); // シグナルのユーザー ID 情報を設定します .si_uid = current->uid; // シグナルを pid に送信します return kill_something_info(sig, &info, pid);}
* シグナルを送信する function/**kill_something_info() は、kill(2) と同じように興味深い方法で pid を解釈します。** POSIX では、kill(-1,sig) が指定されていないと指定されていますが、私たちが持っているもの* はおそらく間違っています。 BSD または SYSV のようにする必要があります。*/// この関数は、さまざまな PID およびプロセスの動作に応じて異なります。 pid は主に 3 つのカテゴリに分けられます: // (1) pid > 0// (2) pid == -1// (3) pid
0) { //pid>0 の場合、信号は 1 つのカテゴリにのみ送信されます。プロセス rcu_read_lock(); ret = kill_pid_info(sig, info, find_vpid(pid)); }
read_lock(&tasklist_lock) if (pid != -1) { //受信プロセス番号
1 && !same_thread_group(p, current)) { int err = group_send_sig_info(sig, info, p) } } ret = count retval : - ESRCH; } read_unlock(&tasklist_lock); return ret;}
. 単一プロセスへのシグナルの送信は、次の関数を通じて実装されます。この関数は単一のプロセスにシグナルを送信するだけであり、プロセスが属するプロセス グループなどは扱いません。int kill_pid_info(int sig, struct siginfo *info, struct pid *pid){ int error = -ESRCH; struct task_struct *p; rcu_read_lock();retry: // pid (task_strcut *) に対応するプロセス記述子構造体を取得しますpid_task(pid, PIDTYPE_PID); // 正常に取得 if (p) { // プロセス記述子 p にシグナルを送信します error = group_send_sig_info(sig, info, p) // シグナルの送信が失敗し、エラーが -ESRCH に等しい場合、リトライ。 // 注: この時点では、プロセスが元のプロセスではない可能性があるため、task_struct を再度取得する必要があります。 if (unlikely(error == -ESRCH)) /* * タスクが途中でハッシュ化されていないため、再試行します。 * タスクが停止している場合、pid_task() は NULL を返します。 * de_thread() と競合すると、 * new leader. */ goto retry; } rcu_read_unlock(); return error;}
. 最後のシグナル送信は send_signal によって実装されます。 static int send_signal(int sig, struct siginfo *info, struct task_struct *t, int group){ struct sigpending *pending;assert_spin_locked(&t->sighand->siglock); // シグナルが送信できるかどうかを確認します。送信する場合、送信されたプロセスが終了状態にある場合は、シグナルを破棄します。 if (!prepare_signal(sig, t)) return 0; // シグナルが単一のプロセスに送信される場合、グループは 1 であり、保留中は共有ブロッキングシグナル処理保留中 = &t->signal->shared_pending: &t ->pending ; /* * 無視されたシグナルをショートし、* 1 つの非 RT シグナルをサポートします。これにより、 * シグナルの原因に関する詳細な情報が得られます */ // シグナルが非 RT シグナルである場合。リアルタイム信号 ( /* リアルタイム シグナルsigqueue またはその他のリアルタイムメカニズムによって送信された場合にキューに入れられるかどうかは、kill() がそうするかどうかで定義されますが、最小の驚きの原則に基づいてそうしようとしますが、kill が EAGAIN で失敗することは許されないためです。メモリが少ない場合は、少なくとも 1 つのシグナルが配信されることを確認し、情報構造体は渡さないようにします。 */ q = __sigqueue_alloc(t, GFP_ATOMIC, (sig 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_code = SI_USER; q->info.si_pid = task_pid_vnr(current); q->info.si_uid = current->uid; SEND_SIG_PRIV: q->info.si_signo; info.si_errno = 0; q->info.si_pid = 0; デフォルト: copy_siginfo(&q->info); } else if (!is_si_special(info)) { if (sig >= SIGRTMIN && info->si_code != SI_USER) /* * キューのオーバーフロー、中止 * シグナルが rt で、ユーザーが何かを使用して送信した場合は中止される可能性があります。 kill() 以外。 */ return -EAGAIN; }out_set: signalfd_notify(t, sig); complete_signal(sig, t, group);
http://www.bkjia.com/PHPjc/1124516.html
www.bkjia.com本当http://www.bkjia.com/PHPjc/1124516.html技術記事 Linux カーネルシグナルの実装と使用法について、以前書いたものをいくつか投稿して皆さんと一緒に学んでいきます。 1. 基本的なデータ構造 * Linux シグナル番号の構造 下の図は、「Linux カーネルを深く理解する...
」です。