【The Design and Implementation of The FreeBSD Operating System】
4.7 Signals 笔记 3
雨丝风片@chinaunix.net
【2. Given an action, p_signal() selects the appropriate thread and adds the signal to the thread's set of pending signals, td_siglist, and then does any implicit actions specific to that signal. For example, if the signal is a continue signal, SIGCONT, any pending signals that would normally cause the process to stop, such as SIGTTOU, are removed.】
【3. Next, psignal() checks whether the signal is being masked. If the thread is currently masking delivery of the signal, psignal()'s work is complete and it may return.】
对psignal()函数进行代码分离的CVS log如下:
Revision 1.220 / (download) - annotate - [select for diffs], Mon Mar 31 22:57:01 2003 UTC (3 years, 7 months ago) by jeff
Branch: MAIN
Changes since 1.219: +0 -0 lines
Diff to previous 1.219 (colored)
- The siglist in the proc holds signals that were blocked by all threads when they were delivered. In signotify() check to see if we have unblocked any of those signals and post them to the thread.
- Use td_sigmask instead of p_sigmask in all cases.
- In sigpending return both signals pending on the thread and proc. - Define a function, sigtd(), that finds the appropriate thread to deliver the signal to if psignal() has been called instead of tdsignal().
- Define a function, tdsignal(), that delivers a signal to a specific thread or if that thread has the signal blocked it may deliver it to the process where it will wait for a thread to unblock it.
- Since we are delivering signals to a specific thread we do not need to abort the sleep of all threads.
- Rename the old tdsignal() to tdsigwakeup().
- Save and restore the old signal mask to and from the thread.
这次是剥离出一个tdsignal()函数负责向特定线程发送信号。在2003年的6月28号又进一步从tdsignal()中分离出do_tdsignal()函数。
在进行了上述代码分离之后,tdsignal()和do_tdsignal()函数都是针对特定线程的了,而选择线程的代码则仍然在psignal()函数中:
---------------------------------------------------------------------FreeBSD6.1
1637 prop = sigprop(sig);
1638
1639 /*
1640 * Find a thread to deliver the signal to.
1641 */
1642 td = sigtd(p, sig, prop);
1643
1644 tdsignal(td, sig, SIGTARGET_P);
-----------------------------------------------/sys/kern/kern_sig.c---psignal()
sigprop()函数的代码如下:
---------------------------------------------------------------------FreeBSD6.1
236 static __inline int
237 sigprop(int sig)
238 {
239
240 if (sig > 0 && sig NSIG)
241 return (sigproptbl[_SIG_IDX(sig)]);
242 return (0);
243 }
-----------------------------------------------/sys/kern/kern_sig.c---sigprop()
sigproptbl[]数组中存放着系统所支持的所有信号的属性(也即默认操作):
---------------------------------------------------------------------FreeBSD6.1
130 /*
131 * Signal properties and actions.
132 * The array below categorizes the signals and their default actions
133 * according to the following properties:
134 */
135 #define SA_KILL 0x01 /* terminates process by default */
136 #define SA_CORE 0x02 /* ditto and coredumps */
137 #define SA_STOP 0x04 /* suspend process */
138 #define SA_TTYSTOP 0x08 /* ditto, from tty */
139 #define SA_IGNORE 0x10 /* ignore by default */
140 #define SA_CONT 0x20 /* continue if suspended */
141 #define SA_CANTMASK 0x40 /* non-maskable, catchable */
142 #define SA_PROC 0x80 /* deliverable to any thread */
143
144 static int sigproptbl[NSIG] = {
145 SA_KILL|SA_PROC, /* SIGHUP */
146 SA_KILL|SA_PROC, /* SIGINT */
147 SA_KILL|SA_CORE|SA_PROC, /* SIGQUIT */
148 SA_KILL|SA_CORE, /* SIGILL */
149 SA_KILL|SA_CORE, /* SIGTRAP */
150 SA_KILL|SA_CORE, /* SIGABRT */
151 SA_KILL|SA_CORE|SA_PROC, /* SIGEMT */
152 SA_KILL|SA_CORE, /* SIGFPE */
153 SA_KILL|SA_PROC, /* SIGKILL */
154 SA_KILL|SA_CORE, /* SIGBUS */
155 SA_KILL|SA_CORE, /* SIGSEGV */
156 SA_KILL|SA_CORE, /* SIGSYS */
157 SA_KILL|SA_PROC, /* SIGPIPE */
158 SA_KILL|SA_PROC, /* SIGALRM */
159 SA_KILL|SA_PROC, /* SIGTERM */
160 SA_IGNORE|SA_PROC, /* SIGURG */
161 SA_STOP|SA_PROC, /* SIGSTOP */
162 SA_STOP|SA_TTYSTOP|SA_PROC, /* SIGTSTP */
163 SA_IGNORE|SA_CONT|SA_PROC, /* SIGCONT */
164 SA_IGNORE|SA_PROC, /* SIGCHLD */
165 SA_STOP|SA_TTYSTOP|SA_PROC, /* SIGTTIN */
166 SA_STOP|SA_TTYSTOP|SA_PROC, /* SIGTTOU */
167 SA_IGNORE|SA_PROC, /* SIGIO */
168 SA_KILL, /* SIGXCPU */
169 SA_KILL, /* SIGXFSZ */
170 SA_KILL|SA_PROC, /* SIGVTALRM */
171 SA_KILL|SA_PROC, /* SIGPROF */
172 SA_IGNORE|SA_PROC, /* SIGWINCH */
173 SA_IGNORE|SA_PROC, /* SIGINFO */
174 SA_KILL|SA_PROC, /* SIGUSR1 */
175 SA_KILL|SA_PROC, /* SIGUSR2 */
176 };
-----------------------------------------------------------/sys/kern/kern_sig.c
sigprop()函数的作用就是根据信号ID从sigproptbl[]数组中取出信号属性。不过,原来在psignal()函数中有此调用,是因为sigtd()函数中会用到入参prop。但目前sigtd()函数已不再使用入参prog,因此,sigtd()函数的入参prop以及psignal()函数中对sigprop()函数的调用均已冗余。
sigtd()函数的代码如下:
---------------------------------------------------------------------FreeBSD6.1
1580 static struct thread *
1581 sigtd(struct proc *p, int sig, int prop)
1582 {
1583 struct thread *td, *signal_td;
1584
1585 PROC_LOCK_ASSERT(p, MA_OWNED);
1586
1587 /*
1588 * Check if current thread can handle the signal without
1589 * switching conetxt to another thread.
1590 */
1591 if (curproc == p && !SIGISMEMBER(curthread->td_sigmask, sig))
1592 return (curthread);
1593 signal_td = NULL;
1594 mtx_lock_spin(&sched_lock);
1595 FOREACH_THREAD_IN_PROC(p, td) {
1596 if (!SIGISMEMBER(td->td_sigmask, sig)) {
1597 signal_td = td;
1598 break;
1599 }
1600 }
1601 if (signal_td == NULL)
1602 signal_td = FIRST_THREAD_IN_PROC(p);
1603 mtx_unlock_spin(&sched_lock);
1604 return (signal_td);
1605 }
-------------------------------------------------/sys/kern/kern_sig.c---sigtd()
1591行,如果入参p是当前进程,且当前线程没有在其thread.td_sigmask中将入参信号sig屏蔽的话,则直接返回当前线程,表示由当前线程来处理这个信号,这样无需进行上下文切换。1595行遍历入参进程p的线程链表,只要找到一个没有屏蔽入参信号sig的线程即终止遍历,返回找到的线程。1601行,如果进程内的所有线程都把这个信号给屏蔽了,则返回线程链表上的第一个线程,实际上,这个信号就只有等待屏蔽被解除的时候了。书中对此的描述如下:
【When a process-related signal is sent (for example, an interrupt), then the kernel searches all the threads associated with the process, searching for one that does not have the signal masked. The signal is delivered to the first thread that is found with the signal unmasked. If all threads associated with the process are masking the signal, then the signal is left in the list of signals pending for the process for later delivery.】
由此可以注意到,FreeBSD的待处理信号列表是有进程和线程之分的,分别为proc.p_siglist和thread.td_siglist。do_tdsignal()函数中的相关区分代码如下:
---------------------------------------------------------------------FreeBSD6.1
1690 /*
1691 * If the signal is blocked and not destined for this thread, then
1692 * assign it to the process so that we can find it later in the first
1693 * thread that unblocks it. Otherwise, assign it to this thread now.
1694 */
1695 if (target == SIGTARGET_TD) {
1696 siglist = &td->td_siglist;
1697 } else {
1698 if (!SIGISMEMBER(td->td_sigmask, sig))
1699 siglist = &td->td_siglist;
1700 else
1701 siglist = &p->p_siglist;
1702 }
-------------------------------------------/sys/kern/kern_sig.c---do_tdsignal()
do_tdsignal()函数的入参target表示入参信号sig是针对进程的还是针对线程的。如果该函数是由psignal()函数调过来的,则target为SIGTARGET_P,表示目标为进程。1695行,如果目标为线程,则下面操作的就是入参线程td的待处理信号列表。如果目标为进程,就要看我们在前面分析过的sigtd()函数的查找结果了。如果入参线程td没有屏蔽这个信号,表示在进程p的线程链表中至少有线程td可以处理这个信号,于是我们就使用这个线程的待处理信号列表。否则就表示进程p的所有线程都已将这个信号屏蔽,我们只有暂时把这个信号加入进程p的待处理信号列表了。
我们继续来看do_tdsignal()函数中的代码:
---------------------------------------------------------------------FreeBSD6.1
1729 if (prop & SA_CONT) {
1730 SIG_STOPSIGMASK(p->p_siglist);
1731 /*
1732 * XXX Should investigate leaving STOP and CONT sigs only in
1733 * the proc's siglist.
1734 */
1735 mtx_lock_spin(&sched_lock);
1736 FOREACH_THREAD_IN_PROC(p, td0)
1737 SIG_STOPSIGMASK(td0->td_siglist);
1738 mtx_unlock_spin(&sched_lock);
1739 }
1740
1741 if (prop & SA_STOP) {
1742 /*
1743 * If sending a tty stop signal to a member of an orphaned
1744 * process group, discard the signal here if the action
1745 * is default; don't stop the process below if sleeping,
1746 * and don't clear any pending SIGCONT.
1747 */
1748 if ((prop & SA_TTYSTOP) &&
1749 (p->p_pgrp->pg_jobc == 0) &&
1750 (action == SIG_DFL))
1751 return;
1752 SIG_CONTSIGMASK(p->p_siglist);
1753 mtx_lock_spin(&sched_lock);
1754 FOREACH_THREAD_IN_PROC(p, td0)
1755 SIG_CONTSIGMASK(td0->td_siglist);
1756 mtx_unlock_spin(&sched_lock);
1757 p->p_flag &= ~P_CONTINUED;
1758 }
1759
1760 SIGADDSET(*siglist, sig);
-------------------------------------------/sys/kern/kern_sig.c---do_tdsignal()
1729行,如果入参信号sig的属性为SA_CONT,则清除进程p自身的以及p中所有线程的待处理信号列表中的SIGSTOP、SIGTSTP、SIGTTIN、SIGTTOU信号。1741行,如果信号的属性为SA_STOP,则清除进程p自身的以及p中所有线程的待处理信号列表中的SIGCONT信号。同时清除进程的P_CONTINUED标志。但如果是向一个孤儿进程组的成员之一发送tty停止信号且操作为缺省值的话,则直接返回。
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/9831/showart_197274.html