4.4bsd定义了31个signal,其default action也分别指定,如下: Name Default action Description SIGHUP terminate process terminal line hangup SIGINT terminate process interrupt program SIGQUIT create core image quit program SIGILL create core image illegal instruction SIGTRAP create core image trace trap SIGIOT create core image I/O trap instruction executed SIGEMT create core image emulate instruction executed SIGFPE create core image floating-point exception SIGKILL terminate process kill program SIGBUS create core image bus error SIGSEGV create core image segmentation violation SIGSYS create core image bad argument to system call SIGPIPE terminate process write on a pipe with no one to read it SIGALRM terminate process real-time timer expired SIGTERM terminate process software termination signal SIGURG discard signal urgent condition on I/O channel SIGSTOP stop process stop signal not from terminal SIGTSTP stop process stop signal from terminal SIGCONT discard signal a stopped process is being continued SIGCHLD discard signal notification to parent on child stop or exit SIGTTIN stop process read on terminal by background process SIGTTOU stop process write to terminal by background process SIGIO discard signal I/O possible on a descriptor SIGXCPU terminate process CPU time limit exceeded SIGXFSZ terminate process file-size limit exceeded SIGVTALRM terminate process virtual timer expired SIGPROF terminate process profiling timer expired SIGWINCH discard signal window size changed SIGINFO discard signal information request SIGUSR1 terminate process user-defined signal 1 SIGUSR2 terminate process user-defined signal 2 4.4bsd实现的是所谓的reliable signal,所谓的可靠信号,是指具有下面特性的signal:signal handler一旦安装,除非重新安装否则一直有效,这样一个信号产生发送被处理后,不需要重新安装这个handler,而早期unix实现的unreliable signal不具备这个特性,对于unreliable signal,你安装一个handler,当该信号产生后,内核会自动reset该信号的处理函数为default action,这导致很多问题。 此外,reliable signal可以被mask,一个signal一旦被mask,那么下次产生该signal时候,内核只记录它但是不 post它,直到该信号被unmask,这个时候这个signal才会被post并处理
/* * Process signal actions and state, needed only within the process * (not necessarily resident). */ structsigacts { sig_tps_sigact[NSIG];/* disposition of signals */ sigset_t ps_catchmask[NSIG];/* signals to be blocked */ sigset_t ps_sigonstack;/* signals to take on sigstack */ sigset_t ps_sigintr;/* signals that interrupt syscalls */ sigset_t ps_oldmask;/* saved mask from before sigpause */ intps_flags;/* signal flags, below */ structsigaltstack ps_sigstk;/* sp & on stack state variable */ intps_sig;/* for core dump/debugger XXX */ longps_code;/* for core dump/debugger XXX */ longps_addr;/* for core dump/debugger XXX */ sigset_t ps_usertramp;/* SunOS compat; libc sigtramp XXX */ }; 3. struct sigaction---描述signal handler structsigaction { void(*sa_handler)(int);/* signal handler *///signal处理函数 sigset_t sa_mask;/* signal mask to apply *///signal的mask intsa_flags;/* see signal options below */ }; 这个地方唯一没搞清楚的是,在进程描述符中有个p_sigacts的域(即一个struct sigacts的指针),还有描述signal catch ignore的域:p_sigcatch,这个域和sigacts里面的ps_catchmask好象重复了,不知道为什么弄两个?
setsigvec()完成实际的设置signal handler工作,代码如下: void setsigvec(p, signum, sa) register struct proc *p;//进程描述符,我们将要设置它的signal handler int signum;//哪个signal?我们将设置这个signal的handler register struct sigaction *sa;//指向新的handler { register struct sigacts *ps = p->p_sigacts; register int bit; 1bit = sigmask(signum); /* * Change setting atomically. */ 2(void) splhigh(); //提高中断level,禁止所有中断,同步用,为了保护下面这段代码不被打断 3ps->ps_sigact[signum] = sa->sa_handler;//设置该进程的 4ps->ps_catchmask[signum] = sa->sa_mask &~ sigcantmask;//设置进程的signal mask 5if ((sa->sa_flags & SA_RESTART) == 0) 6ps->ps_sigintr |= bit;//如果该信号没有设置SA_RESTART,则它是属于可中断系统调用的signal,把它加到ps_sigintr 7else 8ps->ps_sigintr &= ~bit;//否则,它是restart syscall类型 9if (sa->sa_flags & SA_ONSTACK)//如果该signal是ONSTACK的,加到ps_sigonstack 10ps->ps_sigonstack |= bit; 11else 12ps->ps_sigonstack &= ~bit; #ifdef COMPAT_SUNOS if (sa->sa_flags & SA_USERTRAMP) ps->ps_usertramp |= bit; else ps->ps_usertramp &= ~bit; #endif 13if (signum == SIGCHLD) {//如果SIGCHLD,做特殊处理 14if (sa->sa_flags & SA_NOCLDSTOP)//如果该信号设置了SA_NOCLDSTOP 15p->p_flag |= P_NOCLDSTOP;//设置p_flag,表示当其子进程exit的时候这个进程不产生SIGCHLD 16else 17p->p_flag &= ~P_NOCLDSTOP; } /* * Set bit in p_sigignore for signals that are set to SIG_IGN, * and for signals set to SIG_DFL where the default is to ignore. * However, don't put SIGCONT in p_sigignore, * as we have to restart the process. */ 18if (sa->sa_handler == SIG_IGN || 19 (sigprop[signum] & SA_IGNORE && sa->sa_handler == SIG_DFL)) { 20p->p_siglist &= ~bit;/* never to be seen again */ 21if (signum != SIGCONT) 22p->p_sigignore |= bit;/* easier in psignal */ 23p->p_sigcatch &= ~bit; 24} else { 25p->p_sigignore &= ~bit; 26if (sa->sa_handler == SIG_DFL) 27p->p_sigcatch &= ~bit; 28else 29p->p_sigcatch |= bit; } 30(void) spl0();//降低interrupt level,出临界区 } 1~17在代码注释中解释过了,特别说下4句,这句设置进程mask 哪些signal。sigcantmask定义为:#definesigcantmask(sigmask(SIGKILL) | sigmask(SIGSTOP)),可见它表示SIGKILL和SIGSTOP是不能被mask的。 18~23处理signal ignore情况,如果一个信号要被ignore,即sa->sa_handler=SIG_IGN或者sigprop[signum]为SA_IGNORE并且sa_handler为default handler,则清除p->p_siglist对应位,p_siglist记录的是这个进程接受到的(但还没有deliver的信号),或者说是这个进程pending的信号,这样这个signal就从进程的就绪signal中去掉了,直接ignore了。如果这个被ignore的信号不是SIGCONT,则把它加到p->p_sigignore,进程描述符的这个标志记录所有被ignore的信号,但是unix对ignore SIGCONT作了特殊处理,如果碰巧这里想ignore的信号是SIGCONT,我们并不把它记录在p->p_sigignore中,这个作用在下面post signal的时候可以看到。 23句把这个signal从p_sigcacth中清除,这样以后该进程不再处理这个signal 24~29处理这个signal没有ignore的情况。 首先清除p_sigignore中对应的位,表示这个信号不被忽略(25句)。如果指定的handler是default handler(26~27句),我们清除p_sigcatch中对应位,表示以后该进程不再catch这个signal,而交给系统默认处理,否则set p_sigcatch对应位。
四.设置进程的signal mask 所谓mask,就是告诉内核哪些signal我不关心,想block掉。4.4bsd提供sigprocmask()系统调用设置进程的mask: /* * Manipulate signal mask. * Note that we receive new mask, not pointer, * and return old mask as return value; * the library stub does the rest. */ int sigprocmask(p, uap, retval) register struct proc *p; struct sigprocmask_args /* { syscallarg(int) how; syscallarg(sigset_t) mask; } */ *uap; register_t *retval; { int error = 0;
信号产生在4.4bsd中是由psignal()实现的,这个是对单个进程而言的,也称为对单个进程post一个signal(也就是产生一个信号),如果需要对一个进程组post signal,则可以用gsignal() /* * Send the signal to the process. If the signal has an action, the action * is usually performed by the target process rather than the caller; we add * the signal to the set of pending signals for the process. * * Exceptions: * o When a stop signal is sent to a sleeping process that takes the * default action, the process is stopped without awakening it. * o SIGCONT restarts stopped processes (or puts them back to sleep) * regardless of the signal action (eg, blocked or ignored). * * Other ignored signals are discarded immediately. */ void psignal(p, signum) register struct proc *p; register int signum; { register int s, prop; register sig_t action; int mask;
1if ((u_int)signum >= NSIG || signum == 0)//如果是无效的signal,panic 2panic("psignal signal number"); 3mask = sigmask(signum);//获得该信号对应的掩码 4prop = sigprop[signum];//获得该信号对应的default action
/* * If proc is traced, always give parent a chance. */ 5if (p->p_flag & P_TRACED)//如果该进程被traced,则总是采用default action 6action = SIG_DFL; 7else { /* * If the signal is being ignored, * then we forget about it immediately. * (Note: we don't set SIGCONT in p_sigignore, * and if it is set to SIG_IGN, * action will be SIG_DFL here.) */ 8if (p->p_sigignore & mask)//如果该信号被忽略,则返回,什么也不做 9return; 10if (p->p_sigmask & mask)//如果该信号被mask,则只记录这个signal,而不deliver它不处理它 11action = SIG_HOLD; 12else if (p->p_sigcatch & mask)//如果该信号被catch,则我们要调用handler处理它 13action = SIG_CATCH; 14else//否则,应用default action 15action = SIG_DFL; }
21if (prop & SA_STOP) { /* * If sending a tty stop signal to a member of an orphaned * process group, discard the signal here if the action * is default; don't stop the process below if sleeping, * and don't clear any pending SIGCONT. */ 22if (prop & SA_TTYSTOP && p->p_pgrp->pg_jobc == 0 && 23 action == SIG_DFL) 24return; 25p->p_siglist &= ~contsigmask;//如果该signal是那几个可以导致进程stop的signal,则这里我们把该进程pending的SIGCONT remove掉 } 26p->p_siglist |= mask;//把该signal加到进程pending signal列表中
/* * Defer further processing for signals which are held, * except that stopped processes must be continued by SIGCONT. */ 27if (action == SIG_HOLD && ((prop & SA_CONT) == 0 || p->p_stat != SSTOP)) 28return; 29s = splhigh(); 30switch (p->p_stat) {//根据进程的当前状态分别处理
31case SSLEEP: /* * If process is sleeping uninterruptibly * we can't interrupt the sleep... the signal will * be noticed when the process returns through * trap() or syscall(). */ 32if ((p->p_flag & P_SINTR) == 0)//如果该进程处于不可中断休眠状态,什么也不做,退出 33goto out; /* * Process is sleeping and traced... make it runnable * so it can discover the signal in issignal() and stop * for the parent. */ 34if (p->p_flag & P_TRACED)//如果进程被trace,则设置它为SRUN状态 35goto run; /* * If SIGCONT is default (or ignored) and process is * asleep, we are finished; the process should not * be awakened. */ 36if ((prop & SA_CONT) && action == SIG_DFL) { 37p->p_siglist &= ~mask;//如果这是个SIGCONT,并且我们ignore它或者没有catch它,则保持进程处于sleep状态,清除这个signal 38goto out; } /* * When a sleeping process receives a stop * signal, process immediately if possible. * All other (caught or default) signals * cause the process to run. */ 39if (prop & SA_STOP) {//如果这是个可以导致进程stop的信号 40if (action != SIG_DFL)//如果被catch了,我们唤醒它进入SRUN状态 41goto runfast; /* * If a child holding parent blocked, * stopping could cause deadlock. */ 42if (p->p_flag & P_PPWAIT)//P_PPWAIT表示其父进程正等待在wait4()上,等着它执行或exit 43goto out;//我们直接退出,什么不做,因为如果这里让该进程stop可能导致死锁 44p->p_siglist &= ~mask; 45p->p_xstat = signum; 46if ((p->p_pptr->p_flag & P_NOCLDSTOP) == 0) 47psignal(p->p_pptr, SIGCHLD);//发送SIGCHLD通知父进程我的状态变换了 48stop(p);//stop这个进程 49goto out; 50} else 51goto runfast;//对于所有其他signal,统统唤醒进程,让它进入SRUN状态 /*NOTREACHED*/
52case SSTOP: /* * If traced process is already stopped, * then no further action is necessary. */ 53if (p->p_flag & P_TRACED) 54goto out;
57if (prop & SA_CONT) { /* * If SIGCONT is default (or ignored), we continue the * process but don't leave the signal in p_siglist, as * it has no further action. If SIGCONT is held, we * continue the process and leave the signal in * p_siglist. If the process catches SIGCONT, let it * handle the signal itself. If it isn't waiting on * an event, then it goes back to run state. * Otherwise, process goes back to sleep state. */ 58if (action == SIG_DFL) 59p->p_siglist &= ~mask; 60if (action == SIG_CATCH) 61goto runfast; 62if (p->p_wchan == 0) 63goto run; 64p->p_stat = SSLEEP; 65goto out; }
66if (prop & SA_STOP) { /* * Already stopped, don't need to stop again. * (If we did the shell could get confused.) */ 67p->p_siglist &= ~mask;/* take it away */ 68goto out; }
/* * If process is sleeping interruptibly, then simulate a * wakeup so that when it is continued, it will be made * runnable and can look at the signal. But don't make * the process runnable, leave it stopped. */ 69if (p->p_wchan && p->p_flag & P_SINTR) 70unsleep(p); 71goto out;
default: /* * SRUN, SIDL, SZOMB do nothing with the signal, * other than kicking ourselves if we are running. * It will either never be noticed, or noticed very soon. */ 72if (p == curproc)//如果是当前进程,我们通知它有signal pending 73signotify(p); 74goto out; } /*NOTREACHED*/
runfast: /* * Raise priority to at least PUSER. */ if (p->p_priority > PUSER) p->p_priority = PUSER; run: setrunnable(p); out: splx(s); }
文章评论(0条评论)
登录后参与讨论