psignal()分下面几步post信号: 1. 根据进程描述符里面的p_sigignore, p_sigmask, 和p_sigcatch域确定进程要采取的行为.如果对于这个进程既不block(即mask)也不忽略也不catch,则认为采用default action.(15句),如果被忽略则直接返回什么也不做(8~9句),如果被屏蔽则只记录不deliver(10~11),记录是在26句完成.如果被catch,则我们将在以后调用指定的handler处理它(12~13). 如果进程被traced,则我们总是采用default action(5~6句),但是这个地方有些不明白,因为<the design and implementation of 4.4bsd>中说对于被traced的进程,系统总是让父进程首先进行处理.书中说:"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." 我不明白代码中设action=SIG_DFL,怎么就让父进程获得机会处理它呢? 2. 将信号记录,(26句)即加到进程描述符的p_siglist中.19~25句是对一些特殊情况处理,19~20句判断如果这个信号是SIGCONT,则我们把该进程已经接受还没有处理的那些会引起进程stop的信号从记录中remove p->p_siglist记录的是pending在p这个进程上的那些signal,这些signal以后会在deliver阶段被处理,如果从这里remove掉一个signal,那么等于取消了这个signal.22~24句:如果这个信号是发给一个孤儿进程组的tty stop信号,并且我们没有catch它,则丢弃这个信号,什么也不做. 3. 27~28检查该信号是否被进程mask?如果是,我们的工作完成,退出 4. 根据进程当前状态,分别处理. a.如果当前状态是SSLEEP:如果进程是处于不可中断休眠,则退出(32~33),如果进程被traced,我们让它运行(34~35);如果信号是SIGCONT,并且我们没有catch它,则丢弃这个信号退出(36~38);如果是那些可导致进程stop的信号(比如:SIGSTOP,SIGTSTOP,SIGTTIN,SIGTTOU),如果我们没有catch它,则stop这个进程,同时发送SIGCHLD通知父进程(44~49),如果catch了,则唤醒这个进程退出(40~41句).对于所有的其他信号,我们总是唤醒这个进程(50~51句). b.如果进程处于SSTOP状态: 如果进程是因为被traced而停止的,则什么也不做(53~54句);如果该信号是SIGKILL,则总是唤醒进程(55~56);如果这个信号是一个导致进程stop的信号,我们丢弃这个信号,什么也不做,因为当前这个进程已经处于stop状态了(66~68句);如果该signal是SIGCONT,那么根据我们有没有mask它,有没有catch它情况会不同58~65句) mask否? catch否? 是否在等待事件? 处理方法 没有 没有 有 从p_siglist中清除,进程进入SSLEEP状态 没有 没有 没有 从p_siglist中清除,进程进入SRUN状态 有 没有 有 进程进入SSLEEP状态,但不从p_siglist中清除 有 没有 没有 进程进入SRUN状态,不从p_siglist中清除 X 是 X 进程进入SRUN状态,不从p_siglist中清除 69~71句,如果到这里进程状态为SSLEEP,并且它是在等待事件,而且是可中断的休眠,则唤醒它. c.如果进程当前状态为SRUN, SIDL, SZOMB,则什么也不做,除非此进程正在运行,这个时候我们调用signotify()发送一个AST告诉它有新signal就绪了. NOTE:4句中通过sigprop获得是这个signal的default action,sigprop是一个数组,定义如下,表示每个signal对应的default action是什么. /* * Signal properties and actions. * The array below categorizes the signals and their default actions * according to the following properties: */ #defineSA_KILL0x01/* terminates process by default */ #defineSA_CORE0x02/* ditto and coredumps */ #defineSA_STOP0x04/* suspend process */ #defineSA_TTYSTOP0x08/* ditto, from tty */ #defineSA_IGNORE0x10/* ignore by default */ #defineSA_CONT0x20/* continue if suspended */ #defineSA_CANTMASK0x40/* non-maskable, catchable */
if (pgrp) for (p = pgrp->pg_members.lh_first; p != 0; p = p->p_pglist.le_next) if (checkctty == 0 || p->p_flag & P_CONTROLT) psignal(p, signum); } 很简单就是对进程组的每个进程调用psignal()。但是如果参数checkctty=1,则只对拥有控制terminal的进程发送信号。如果进程描述符的p_flag指定了P_CONTROLT则表示这个进程拥有控制终端。 六.signal的delivery 大多数与信号deliver相关的操作都在进程上下文中进行,每当进程从kernel mode返回user mode的时候,比如从一个系统调用返回的时候,或者从中断handler,trap handler返回的时候都会调用CURSIG宏检查当前进程有没有signal pending,有则deliver它,也就是处理它. 比如对于i386体系结构来说,其系统调用实现在usr\src\sys\i386\i386\trap.c中 /* * syscall(frame): *System call request from POSIX system call gate interface to kernel. * Like trap(), argument is call by reference. */ /*ARGSUSED*/ syscall(frame) volatile struct syscframe frame; { ...............//omit here done: /* * Reinitialize proc pointer `p' as it may be different * if this is a child returning from fork syscall. */ p = curproc; while (i = CURSIG(p)) postsig(i); p->p_priority = p->p_usrpri; .................//omit here } 我们看到在syscall退出的时候用 while (i = CURSIG(p)) postsig(i); 检查是否有pending的信号,有就调用postsig()deliver它. CURSIG(的实现如下: /* * Determine signal that should be delivered to process p, the current * process, 0 if none. If there is a pending stop signal with default * action, the process stops in issignal(). */ #defineCURSIG(p)\ (((p)->p_siglist == 0 ||\ ((p)->p_flag & P_TRACED) == 0 &&\ ((p)->p_siglist & ~(p)->p_sigmask) == 0) ?\ 0 : issignal(p)) 它判断是否有pengding的signal,如果有调用issignal()获得是哪个signal.如果有pending的stop signal,并且我们没有catch它没有mask它,那么直接在issignal()中处理掉. /* * If the current process has received a signal (should be caught or cause * termination, should interrupt current syscall), return the signal number. * Stop signals with default action are processed immediately, then cleared; * they aren't returned. This is checked after each entry to the system for * a syscall or trap (though this can usually be done without calling issignal * by checking the pending signal masks in the CURSIG macro.) The normal call * sequence is * *while (signum = CURSIG(curproc)) *postsig(signum); */ int issignal(p) register struct proc *p; { register int signum, mask, prop;
1for (;;) { 2mask = p->p_siglist & ~p->p_sigmask; 3if (p->p_flag & P_PPWAIT) 4mask &= ~stopsigmask; 5if (mask == 0) /* no signal to send */ 6return (0); 7signum = ffs((long)mask); 8mask = sigmask(signum); 9prop = sigprop[signum]; /* * We should see pending but ignored signals * only if P_TRACED was on when they were posted. */ 10if (mask & p->p_sigignore && (p->p_flag & P_TRACED) == 0) { 11p->p_siglist &= ~mask; 12continue; } 13if (p->p_flag & P_TRACED && (p->p_flag & P_PPWAIT) == 0) { /* * If traced, always stop, and stay * stopped until released by the parent. * * Note that we must clear the pending signal * before we call trace_req since that routine * might cause a fault, calling tsleep and * leading us back here again with the same signal. * Then we would be deadlocked because the tracer * would still be blocked on the ipc struct from * the initial request. */ 14p->p_xstat = signum; 15p->p_siglist &= ~mask; 16psignal(p->p_pptr, SIGCHLD); 17do { 18stop(p); 19mi_switch(); 20} while (!trace_req(p) && p->p_flag & P_TRACED);
/* * If parent wants us to take the signal, * then it will leave it in p->p_xstat; * otherwise we just look for signals again. */ 21signum = p->p_xstat; 22if (signum == 0) 23continue;
/* * Put the new signal into p_siglist. If the * signal is being masked, look for other signals. */ 24mask = sigmask(signum); 25p->p_siglist |= mask; 26if (p->p_sigmask & mask) 27continue;
/* * If the traced bit got turned off, go back up * to the top to rescan signals. This ensures * that p_sig* and ps_sigact are consistent. */ 28if ((p->p_flag & P_TRACED) == 0) 29continue; }
/* * Decide whether the signal should be returned. * Return the signal's number, or fall through * to clear it from the pending mask. */ 30switch ((long)p->p_sigacts->ps_sigact[signum]) {
31case (long)SIG_DFL: /* * Don't take default actions on system processes. */ 32if (p->p_pid <= 1) { #ifdef DIAGNOSTIC /* * Are you sure you want to ignore SIGSEGV * in init? XXX */ printf("Process (pid %d) got signal %d\n", p->p_pid, signum); #endif 33break;/* == ignore */ } /* * If there is a pending stop signal to process * with default action, stop here, * then clear the signal. However, * if process is member of an orphaned * process group, ignore tty stop signals. */ 34if (prop & SA_STOP) { 35if (p->p_flag & P_TRACED || 36 (p->p_pgrp->pg_jobc == 0 && 37 prop & SA_TTYSTOP)) 38break;/* == ignore */ 39p->p_xstat = signum; 40stop(p); 41if ((p->p_pptr->p_flag & P_NOCLDSTOP) == 0) 42psignal(p->p_pptr, SIGCHLD); 43mi_switch(); 44break; 45} else if (prop & SA_IGNORE) { /* * Except for SIGCONT, shouldn't get here. * Default action is to ignore; drop it. */ 46break;/* == ignore */ 47} else 48return (signum); /*NOTREACHED*/
49case (long)SIG_IGN: /* * Masking above should prevent us ever trying * to take action on an ignored signal other * than SIGCONT, unless process is traced. */ 50if ((prop & SA_CONT) == 0 && 51 (p->p_flag & P_TRACED) == 0) 52printf("issignal\n"); 53break;/* == ignore */
54default: /* * This signal has an action, let * postsig() process it. */ 55return (signum); } 56p->p_siglist &= ~mask;/* take the signal! */ } /* NOTREACHED */ } 2句获得进程未被mask的就绪signal.3~4句判断其父进程是否在等待它exit或exec,如果是那么我们不能处理可引起该进程stop 的信号,因为这些信号可能导致父进程和子进程的deadlock。5~6句:如果没有需要处理的信号,我们直接退出。 7句:得到mask这个变量第一个非0的位,也就是得到第一个pending的信号,ffs()这个函数从mask这个数的bit 0开始扫描,直到找到第一个不是0的位,返回这个位号(即如果bit9=1,则返回10。代码如下: int ffs(mask) register int mask; { register int bit;
Postsig(): /* * Take the action for the specified signal * from the current set of pending signals. */ void postsig(signum) register int signum; { register struct proc *p = curproc; register struct sigacts *ps = p->p_sigacts; register sig_t action; u_long code; int mask, returnmask;
#ifdef DIAGNOSTIC if (signum == 0) panic("postsig"); #endif 1mask = sigmask(signum); 2p->p_siglist &= ~mask; 3action = ps->ps_sigact[signum]; #ifdef KTRACE 4if (KTRPOINT(p, KTR_PSIG)) 5ktrpsig(p->p_tracep, 6 signum, action, ps->ps_flags & SAS_OLDMASK ? 7 ps->ps_oldmask : p->p_sigmask, 0); #endif 8if (action == SIG_DFL) { /* * Default action, where the default is to kill * the process. (Other cases were ignored above.) */ 9sigexit(p, signum); /* NOTREACHED */ 10} else { /* * If we get here, the signal must be caught. */ #ifdef DIAGNOSTIC if (action == SIG_IGN || (p->p_sigmask & mask)) panic("postsig action"); #endif /* * Set the new mask value and also defer further * occurences of this signal. * * Special case: user has done a sigpause. Here the * current mask is not of interest, but rather the * mask from before the sigpause is what we want * restored after the signal processing is completed. */ 11(void) splhigh(); 12if (ps->ps_flags & SAS_OLDMASK) { 13returnmask = ps->ps_oldmask; 14ps->ps_flags &= ~SAS_OLDMASK; 15} else 16returnmask = p->p_sigmask; 17p->p_sigmask |= ps->ps_catchmask[signum] | mask; 18(void) spl0(); 19p->p_stats->p_ru.ru_nsignals++; 20if (ps->ps_sig != signum) { 21code = 0; 22} else { 23code = ps->ps_code; 24ps->ps_code = 0; 25ps->ps_sig = 0; 26} 27sendsig(action, signum, returnmask, code); } } 8~9句:对于那些default action的信号,我们调用sigexit() kill这个进程(可能根据需要生成core dump)。前面已经说过Unix中定义的信号default action有3种,一种是忽略,这种情况在前面issignal()中已经处理掉了(参见前面issignal()45~46句),所以到这里碰到的所有default action一定是kill process的。 Sigexit()杀死进程,同时对于某些信号还生成core dump,代码如下: /* * Force the current process to exit with the specified signal, dumping core * if appropriate. We bypass the normal tests for masked and caught signals, * allowing unrecoverable failures to terminate the process without changing * signal state. Mark the accounting record with the signal termination. * If dumping core, save the signal number for the debugger. Calls exit and * does not return. */ void sigexit(p, signum) register struct proc *p; int signum; {
p->p_acflag |= AXSIG; if (sigprop[signum] & SA_CORE) { p->p_sigacts->ps_sig = signum; if (coredump(p) == 0) signum |= WCOREFLAG; } exit1(p, W_EXITCODE(0, signum)); /* NOTREACHED */ } 10~27句:调用sendsig()去执行signal handler,sendsig()是平台相关的,调用它后,当进程从kernel mode return to user mode的时候就会执行这些handler。 17句:设置新的sig mask,那个ps->ps_catchmask[signum]是在sigaction里面设的,前面提到我对这个域的作用感到困惑,可能唯一用到它的就在这里。ps->ps_catchmask是一个32元素的数组,它对每个signal都对应一个元素,这个元素记录在用户调用sigaction()安装handler的时候,指定的附加signal mask,所谓的附加mask,是指在原先进程的signal mask(记录在进程描述符的p_sigmask中)基础上临时增加的其他mask。这些附加的(additional)mask在signal handler执行前被加到进程的p_sigmask中,当signal handler执行完成后再恢复到原先的mask(即把那些附加的mask取消)。这里第17句就是把这些附加的mask临时加到进程p_sigmask上。而12~16句计算的returnmask作为一个参数传给sendsig(),则是保存了原先进程的p_sigmask,当sengsig()执行完后会恢复进程的mask为returnmask。 12~14句:如果用户先前调用sigsuspend()pause了进程,那么在pause进程的时候会把 pause前的进程p_sigmask保存在ps-> ps_oldmask中,对于这种情况,我们在这里要做特殊处理。 因为我们在这里接受到一个signal,被pause的进程只有接受到信号才能唤醒,这个时候当这个进程唤醒后执行完signal handler后,我们想恢复的进程信号mask并不是当前保存在p_sigmask中的值,而是进程被pause前的值,所以我们这里把returnmask设为ps->ps_oldmask。
提到sigpause,我们来看下sigsuspend()这个系统调用,它可以将进程pause(即设为SSLEEP状态),同时设置进程signal mask,而且这两个操作是原子的。 Sigsuspend() /* * Suspend process until signal, providing mask to be set * in the meantime. Note nonstandard calling convention: * libc stub passes mask, not pointer, to save a copyin. */ /* ARGSUSED */ int sigsuspend(p, uap, retval) register struct proc *p; struct sigsuspend_args /* { syscallarg(int) mask;//进程的signal mask将设为这个值 } */ *uap; register_t *retval; { 1register struct sigacts *ps = p->p_sigacts;
/* * When returning from sigpause, we want * the old mask to be restored after the * signal handler has finished. Thus, we * save it here and mark the sigacts structure * to indicate this. */ 2ps->ps_oldmask = p->p_sigmask;//保存进程原先的mask 3ps->ps_flags |= SAS_OLDMASK;//设ps_flags标志 4p->p_sigmask = SCARG(uap, mask) &~ sigcantmask; 5while (tsleep((caddr_t) ps, PPAUSE|PCATCH, "pause", 0) == 0) /* void */; /* always return EINTR rather than ERESTART... */ 6return (EINTR); } 3句设置ps_flags,这样在postsig()里面就知道刚刚用户调用sigsuspend() pause进程了。 4句:设置进程信号mask为指定的值,注意不能mask SIGKILL和SIGSTOP 5句:进程sleep 6句:进程被唤醒(由于接受到信号),返回EINTR 剩下来的工作由sendsig()继续,所有这些工作都是平台相关的,这几天忙,没有时间仔细看,先放在这里,以后再说。
文章评论(0条评论)
登录后参与讨论