公开学习文档

公开学习文档


信号

<h2>基础</h2> <ol> <li> <p>1-31 为常规信号;32-64 为实时信号。</p> </li> <li> <p>如果常规信号被连接发送多次,则只有其中的一个发送到进程。实时信号可以排队,以便多个信号都可以被接收到。</p> </li> <li> <p>已经产生但还没有传递的信号称为 挂起信号;同类型的常规信号只能挂起一个,但实时信号可以挂起多个。</p> </li> <li>信号处理函数,一般不会再次被信号中断。</li> </ol> <h2>相关结构</h2> <pre><code class="language-c">// file: E:\ProgramWork\linux\linux-2.6.11\include\linux\sched.h struct task_struct { /* 仅显示部分相关字段 */ /* signal handlers */ struct signal_struct *signal; struct sighand_struct *sighand; // 信号处理函数,由长度为 64 的数组存放每个函数 sigset_t blocked, real_blocked; // 64 位的位图 struct sigpending pending; // 存放私有(特定进程信号)挂起的信号的数据结构 } /* * NOTE! &amp;quot;signal_struct&amp;quot; does not have it's own * locking, because a shared signal_struct always * implies a shared sighand_struct, so locking * sighand_struct is always a proper superset of * the locking of signal_struct. */ struct signal_struct { atomic_t count; atomic_t live; wait_queue_head_t wait_chldexit; /* for wait4() */ /* current thread group signal load-balancing target: */ task_t *curr_target; /* shared signal handling: */ struct sigpending shared_pending; // 存放共享(线程组信号)挂起信号的数据结构 /* thread group exit support */ int group_exit_code; /* overloaded: * - notify group_exit_task when -&amp;gt;count is equal to notify_count * - everyone except group_exit_task is stopped during signal delivery * of fatal signals, group_exit_task processes the signal. */ struct task_struct *group_exit_task; int notify_count; /* thread group stop support, overloads group_exit_code too */ int group_stop_count; unsigned int flags; /* see SIGNAL_* flags below */ /* POSIX.1b Interval Timers */ struct list_head posix_timers; /* job control IDs */ pid_t pgrp; pid_t tty_old_pgrp; pid_t session; /* boolean value for session group leader */ int leader; struct tty_struct *tty; /* NULL if no tty */ /* * Cumulative resource counters for dead threads in the group, * and for reaped dead child processes forked by this group. * Live threads maintain their own counters and add to these * in __exit_signal, except for the group leader. */ cputime_t utime, stime, cutime, cstime; unsigned long nvcsw, nivcsw, cnvcsw, cnivcsw; unsigned long min_flt, maj_flt, cmin_flt, cmaj_flt; /* * We don't bother to synchronize most readers of this at all, * because there is no reader checking a limit that actually needs * to get both rlim_cur and rlim_max atomically, and either one * alone is a single word that can safely be read normally. * getrlimit/setrlimit use task_lock(current-&amp;gt;group_leader) to * protect this instead of the siglock, because they really * have no need to disable irqs. */ struct rlimit rlim[RLIM_NLIMITS]; }; struct sighand_struct { atomic_t count; // 共享该结构的进程个数 struct k_sigaction action[_NSIG]; // 每个信号作为对应的 action spinlock_t siglock; }; struct sigpending { struct list_head list; // 链表上挂的是 sigqueue sigset_t signal; // 位图 }; // file: E:\ProgramWork\linux\linux-2.6.11\include\asm-x86_64\signal.h struct sigaction { __sighandler_t sa_handler; // 执行的操作类型,可以是函数指针,也可以是 SIG_DEF、SIG_IGN unsigned long sa_flags; __sigrestore_t sa_restorer; sigset_t sa_mask; /* mask last for extensibility */ }; </code></pre> <p>这里私有信号与共享信号的区别:</p> <ul> <li>私有信号 特定进程(轻量级进程)的信号,可由 <code>tkill()</code>、<code>tgkill()</code> 发送</li> <li>共享信号 整个线程组的信号,可由 <code>kill()</code>、<code>rt_sigqueueinfo()</code> 发送</li> </ul> <pre><code class="language-c">// file: E:\ProgramWork\linux\linux-2.6.11\include\linux\signal.h /* * Real Time signals may be queued. */ struct sigqueue { struct list_head list; spinlock_t *lock; int flags; siginfo_t info; // 描述产生信号的事件,其中有 si_signo 等信息 struct user_struct *user; };</code></pre> <pre><code class="language-c">// file: E:\ProgramWork\linux\linux-2.6.11\include\linux\sched.h // 判断进程是否有非阻塞的挂起信号 static inline int signal_pending(struct task_struct *p) { return unlikely(test_tsk_thread_flag(p,TIF_SIGPENDING)); // 检查 p-&amp;gt;thread_info-&amp;gt;flags 的 TIF_SIGPENDING 标记 } // file: E:\ProgramWork\linux\linux-2.6.11\kernel\signal.c // 检查进程信号 fastcall void recalc_sigpending_tsk(struct task_struct *t) { if (t-&amp;gt;signal-&amp;gt;group_stop_count &amp;gt; 0 || // ? PENDING(&amp;amp;t-&amp;gt;pending, &amp;amp;t-&amp;gt;blocked) || // 私有信号 PENDING(&amp;amp;t-&amp;gt;signal-&amp;gt;shared_pending, &amp;amp;t-&amp;gt;blocked)) // 共享信号 set_tsk_thread_flag(t, TIF_SIGPENDING); // 设置标记 else clear_tsk_thread_flag(t, TIF_SIGPENDING); // 清除标记 }</code></pre> <pre><code class="language-c">// file: E:\ProgramWork\linux\linux-2.6.11\kernel\signal.c // 向指定进程发送信号 // sig:信号编号,info:0-2 或 siginfo_t 地址,t:目标进程 static int specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t) { int ret = 0; if (!irqs_disabled()) BUG(); assert_spin_locked(&amp;amp;t-&amp;gt;sighand-&amp;gt;siglock); if (((unsigned long)info &amp;gt; 2) &amp;amp;&amp;amp; (info-&amp;gt;si_code == SI_TIMER)) // 大于 2 说明是 siginfo_t 地址 /* * Set up a return to indicate that we dropped the signal. */ ret = info-&amp;gt;si_sys_private; /* Short-circuit ignored signals. */ if (sig_ignored(t, sig)) // 信号没有跟踪、阻塞,并且对应的处理函数是忽略动作,则忽略 goto out; /* Support queueing exactly one non-rt signal, so that we can get more detailed information about the cause of the signal. */ if (LEGACY_QUEUE(&amp;amp;t-&amp;gt;pending, sig)) // 如果信号是常规信号(即 &amp;lt; 32),如果在 pending 中已经有了,说明已经挂起一个信号,则本次忽略 goto out; ret = send_signal(sig, info, t, &amp;amp;t-&amp;gt;pending); // 把信号添加到进程的挂起信号集合中。进入到这里,说明是要增加挂起信号的。 if (!ret &amp;amp;&amp;amp; !sigismember(&amp;amp;t-&amp;gt;blocked, sig)) // 如果执行成功,则进一步检查:如果信号没有被阻塞,则通知进程有新的挂起信号 signal_wake_up(t, sig == SIGKILL); out: return ret; } /* * Tell a process that it has a new active signal.. * * NOTE! we rely on the previous spin_lock to * lock interrupts for us! We can only be called with * &amp;quot;siglock&amp;quot; held, and the local interrupt must * have been disabled when that got acquired! * * No need to set need_resched since signal event passing * goes through -&amp;gt;blocked */ void signal_wake_up(struct task_struct *t, int resume) { unsigned int mask; set_tsk_thread_flag(t, TIF_SIGPENDING); // 在 t-&amp;gt;thread_info-&amp;gt;flags 设置 TIF_SIGPENDING 标记 /* * For SIGKILL, we want to wake it up in the stopped/traced case. * We don't check t-&amp;gt;state here because there is a race with it * executing another processor and just now entering stopped state. * By using wake_up_state, we ensure the process will wake up and * handle its death signal. */ mask = TASK_INTERRUPTIBLE; if (resume) mask |= TASK_STOPPED | TASK_TRACED; if (!wake_up_state(t, mask)) // 如果进程在 mask 状态下,则尝试唤醒进程 kick_process(t); } static int send_signal(int sig, struct siginfo *info, struct task_struct *t, struct sigpending *signals) { struct sigqueue * q = NULL; int ret = 0; /* * fast-pathed signals for kernel-internal things like SIGSTOP * or SIGKILL. */ if ((unsigned long)info == 2) // 如果是 SIGKILL 或 SIGSTOP 则不用挂起,立即执行 goto out_set; /* Real-time signals must be queued if sent by sigqueue, or some other real-time mechanism. It is implementation defined whether kill() does so. We attempt to do so, on the principle of least surprise, but since kill is not allowed to fail with EAGAIN when low on memory we just make sure at least one signal gets delivered and don't pass on the info struct. */ q = __sigqueue_alloc(t, GFP_ATOMIC); // 申请信号资源,如果资源超限,则返回 NULL if (q) { list_add_tail(&amp;amp;q-&amp;gt;list, &amp;amp;signals-&amp;gt;list); switch ((unsigned long) info) { case 0: q-&amp;gt;info.si_signo = sig; q-&amp;gt;info.si_errno = 0; q-&amp;gt;info.si_code = SI_USER; q-&amp;gt;info.si_pid = current-&amp;gt;pid; q-&amp;gt;info.si_uid = current-&amp;gt;uid; break; case 1: q-&amp;gt;info.si_signo = sig; q-&amp;gt;info.si_errno = 0; q-&amp;gt;info.si_code = SI_KERNEL; q-&amp;gt;info.si_pid = 0; q-&amp;gt;info.si_uid = 0; break; default: copy_siginfo(&amp;amp;q-&amp;gt;info, info); break; } } else { if (sig &amp;gt;= SIGRTMIN &amp;amp;&amp;amp; info &amp;amp;&amp;amp; (unsigned long)info != 1 &amp;amp;&amp;amp; info-&amp;gt;si_code != SI_USER) // 资源已经满了,或者没有内存的情况下,如果是实时信号,已经被内核函数发送 ? /* * Queue overflow, abort. We may abort if the signal was rt * and sent by user using something other than kill(). */ return -EAGAIN; if (((unsigned long)info &amp;gt; 1) &amp;amp;&amp;amp; (info-&amp;gt;si_code == SI_TIMER)) /* * Set up a return to indicate that we dropped * the signal. */ ret = info-&amp;gt;si_sys_private; } out_set: sigaddset(&amp;amp;signals-&amp;gt;signal, sig); // 设置位图 return ret; }</code></pre> <h2>传递信号</h2> <p>内核在允许进程恢复用户态下的执行之前,检查进程 TIF_SIGPENDING 标志的值。每当内核处理完一个中断或异常时,就检查是否存在挂起信号。为了处理非阻塞的挂起信号,内核调用 <code>do_signal()</code> 函数。</p> <p>&gt; 阻塞信号相关文章:<a href="https://www.cnblogs.com/zhanggaofeng/p/6087098.html">https://www.cnblogs.com/zhanggaofeng/p/6087098.html</a></p> <p>&gt; 其它文章:<a href="https://blog.csdn.net/qq_34231329/article/details/125535089">https://blog.csdn.net/qq_34231329/article/details/125535089</a></p>

页面列表

ITEM_HTML