【The work of psignal() is a patchwork of special cases required by the process-debugging and job-control facilities and by intrinsic properties associated with signals. The steps involved in posting a signal are as follows:】
【1、Determine the action that the receiving process will take when the signal is delivered. This information is kept in the p_sigignore and p_sigcatch fields of the process's process structure. If a process is not ignoring or catching a signal, the default action is presumed to apply. If a process is being traced by its parent--that is, by a debugger--the parent process is always permitted to intercede before the signal is delivered. If the process is ignoring the signal, psignal()'s work is done and the routine can return.】
在FreeBSD6.1中,上述处理的代码在do_tdsignal()函数中。
---------------------------------------------------------------------FreeBSD6.1
1704 /*
1705 * If the signal is being ignored,
1706 * then we forget about it immediately.
1707 * (Note: we don't set SIGCONT in ps_sigignore,
1708 * and if it is set to SIG_IGN,
1709 * action will be SIG_DFL here.)
1710 */
1711 mtx_lock(&ps->ps_mtx);
1712 if (SIGISMEMBER(ps->ps_sigignore, sig) ||
1713 (p->p_flag & P_WEXIT)) {
1714 mtx_unlock(&ps->ps_mtx);
1715 return;
1716 }
1717 if (SIGISMEMBER(td->td_sigmask, sig))
1718 action = SIG_HOLD;
1719 else if (SIGISMEMBER(ps->ps_sigcatch, sig))
1720 action = SIG_CATCH;
1721 else
1722 action = SIG_DFL;
1723 if (SIGISMEMBER(ps->ps_sigintr, sig))
1724 intrval = EINTR;
1725 else
1726 intrval = ERESTART;
1727 mtx_unlock(&ps->ps_mtx);
-----------------------------------------------------------/sys/kern/kern_sig.c
首先来看SIGISMEMBER宏,其定义如下:
---------------------------------------------------------------------FreeBSD6.1
130 #define SIGISMEMBER(set, signo) \
131 ((set).__bits[_SIG_WORD(signo)] & _SIG_BIT(signo))
-----------------------------------------------------------/sys/sys/signalvar.h
其中,_SIG_WORD()和_SIG_BIT()宏以及__sigset类型的定义为:
---------------------------------------------------------------------FreeBSD6.1
44 #define _SIG_WORDS 4
......
46 #define _SIG_IDX(sig) ((sig) - 1)
47 #define _SIG_WORD(sig) (_SIG_IDX(sig) >> 5)
48 #define _SIG_BIT(sig) (1 << (_SIG_IDX(sig) & 31))
......
51 typedef struct __sigset {
52 __uint32_t __bits[_SIG_WORDS];
53 } __sigset_t;
-------------------------------------------------------------/sys/sys/_sigset.h
__sigset是一个由4个__uint32_t组成的数组。_SIG_WORD()宏根据信号值计算出该信号在数组内的下标。_SIG_BIT()宏则计算出该信号在__uint32_t内的位偏移。于是,SIGISMEMBER()宏的作用就是测试信号signo在信号集set中的对应位是否为1。
前引书云,“Determine the action that the receiving process will take”所需的信息是存放在进程结构体的p_sigignore和p_sigcatch中的。这里说的其实是4.4BSD的情况,代码如下:
-------------------------------------------------------------------4.4BSD-Lite2
81 struct proc {
......
151 sigset_t p_sigmask; /* Current signal mask. */
152 sigset_t p_sigignore; /* Signals being ignored. */
153 sigset_t p_sigcatch; /* Signals being caught by user. */
----------------------------------------------------------------/sys/sys/proc.h
相应的处理代码则是:
-------------------------------------------------------------------4.4BSD-Lite2
689 /*
690 * If the signal is being ignored,
691 * then we forget about it immediately.
692 * (Note: we don't set SIGCONT in p_sigignore,
693 * and if it is set to SIG_IGN,
694 * action will be SIG_DFL here.)
695 */
696 if (p->p_sigignore & mask)
697 return;
698 if (p->p_sigmask & mask)
699 action = SIG_HOLD;
700 else if (p->p_sigcatch & mask)
701 action = SIG_CATCH;
702 else
703 action = SIG_DFL;
-------------------------------------------------------/sys/sys/kern/kern_sig.c
而在FreeBSD中,对这些字段进行了封装,放到了struct sigacts里面:
---------------------------------------------------------------------FreeBSD6.1
46 /*
47 * Logical process signal actions and state, needed only within the process
48 * The mapping between sigacts and proc structures is 1:1 except for rfork()
49 * processes masquerading as threads which use one structure for the whole
50 * group. All members are locked by the included mutex. The reference count
51 * and mutex must be last for the bcopy in sigacts_copy() to work.
52 */
53 struct sigacts {
54 sig_t ps_sigact[_SIG_MAXSIG]; /* Disposition of signals. */
55 sigset_t ps_catchmask[_SIG_MAXSIG]; /* Signals to be blocked. */
56 sigset_t ps_sigonstack; /* Signals to take on sigstack. */
57 sigset_t ps_sigintr; /* Signals that interrupt syscalls. */
58 sigset_t ps_sigreset; /* Signals that reset when caught. */
59 sigset_t ps_signodefer; /* Signals not masked while handled. */
60 sigset_t ps_siginfo; /* Signals that want SA_SIGINFO args. */
61 sigset_t ps_sigignore; /* Signals being ignored. */
62 sigset_t ps_sigcatch; /* Signals being caught by user. */
63 sigset_t ps_freebsd4; /* signals using freebsd4 ucontext. */
64 sigset_t ps_osigset; /* Signals using <= 3.x osigset_t. */
65 sigset_t ps_usertramp; /* SunOS compat; libc sigtramp. XXX */
66 int ps_flag;
67 int ps_refcnt;
68 struct mtx ps_mtx;
69 };
-----------------------------------------------------------/sys/sys/signalvar.h
现在,出现在proc结构体定义代码中的就是sigacts结构体了:
---------------------------------------------------------------------FreeBSD6.1
514 struct proc {
......
525 struct sigacts *p_sigacts; /* (x) Signal actions, state (CPU). */
----------------------------------------------------------------/sys/sys/proc.h
回到前述do_tdsignal()函数的代码。1712行判断入参信号sig是否已在proc.sigacts.ps_sigignore中被设置为忽略了。若是,则函数返回,不做任何处理。另外,如果当前进程正处于退出过程中,亦照此办理。1717行判断入参信号sig是否已在在thread.td_sigmask中被设置为屏蔽了。若是,则对应操作为SIG_HOLD,暂缓对此信号的处理,直至其解除屏蔽。注意,在4.4BSD中,信号掩码字段的名称是p_sigmask,它是放在进程结构体中的,而在FreeBSD中,这个字段的名称已经变成了td_sigmask,被放到线程结构体中了。由此可见,FreeBSD对信号的屏蔽设置是以线程为单位的,而对信号的忽略和捕获设置则仍然以进程为单位。1719行判断入参信号sig是否已在proc.sigacts.ps_sigcatch中被设置为捕获了。若是,则对应操作为SIG_CATCH。如果上述情况都不满足,则在1722行将对应操作设置为SIG_DFL。