可靠信号与不可靠信号
信号分为可靠信号与不可靠信号,可靠信号又称为实时信号,非可靠信号又称为非实时信号,信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号。
不可靠信号的特点在于:
每次信号处理完之后,就会恢复成默认处理,如果希望后面可以再次捕捉信号,需要重新注册。
存在信号丢失的问题,进程收到的信号不作排队处理,相同的信号多次到来会合并为一个。
造成这种情况的原因主要在于Unix对信号处理比较简单,linux继承了Unix的信号处理,不过在后面进行了优化,出现了可靠信号。
信号产生
硬件方式:
用户输入:比如用户输入ctrl+c,就发送了一个SIGINT信号给当前的进程
硬件异常:CPU检测到内存非法访问等异常,通知内核生成相应信号,并发送给发生事件的进程。
软件方式:
命令行输入kill,发送对应类型的信号给指定的进程
进程调用系统调用,发送信号给其他进程
信号处理
在进程表的表项中有一个软中断信号域,该域中每一位对应一个信号,当有信号发送给进程时,对应位置位。因此,进程对不同的信号可以同时保留,但对于同一个信号,进程并不知道在处理之前来过多少个,也就是不可靠信号的信号丢失。
内 核给一个进程发送软中断信号的方法,是在进程所在的进程表项的信号域设置对应于该信号的位。
内核处理一个进程收到的信号的时机是在一个进程从内核态返回用户态时。所以,当一个进程在内核态下运行时,软中断信号并不立即起作用,要等到将返回用户态时才处理。进程只有处理完信号才会返回用户态,进程在用户态下不会有未处理完的信号。
需要注意的一些地方:
1. 在一些系统中,当一个进程处理完中断信号返回用户态之前,内核清除用户区中设 定的对该信号的处理例程的地址,即下一次进程对该信号的处理方法又改为默认值,除非在下一次信号到来之前再次使用signal系统调用。这可能会使得进程 在调用signal之前又得到该信号而导致退出。在BSD中,内核不再清除该地址。但不清除该地址可能使得进程因为过多过快的得到某个信号而导致堆栈溢 出。为了避免出现上述情况。在BSD系统中,内核模拟了对硬件中断的处理方法,即在处理某个中断时,阻止接收新的该类中断。
2. 如果要捕捉的信号发生于进程正在一个系统调用中时,并且该进程睡眠在可中断的优先级上,这时该信号引起进程作一次longjmp,跳出睡眠 状态,返回用户态并执行信号处理例程。当从信号处理例程返回时,进程就象从系统调用返回一样,但返回了一个错误代码,指出该次系统调用曾经被中断。这要注 意的是,BSD系统中内核可以自动地重新开始系统调用。
3. 若进程睡眠在可中断的优先级上,则当它收到一个要忽略的信号时,该进程被唤醒,但不做longjmp,一般是继续睡眠。但用户感觉不到进程曾经被唤醒,而是象没有发生过该信号一样。
4. 内核对子进程终止(SIGCLD)信号的处理方法与其他信号有所区别。当进程检查出收到了一个子进程终止的信号时,缺省情况下,该进程 就象没有收到该信号似的,如果父进程执行了系统调用wait,进程将从系统调用wait中醒来并返回wait调用,执行一系列wait调用的后续操作(找 出僵死的子进程,释放子进程的进程表项),然后从wait中返回。SIGCLD信号的作用是唤醒一个睡眠在可被中断优先级上的进程。如果该进程捕捉了这个 信号,就象普通信号处理一样转到处理例程。如果进程忽略该信号,那么系统调用wait的动作就有所不同,因为SIGCLD的作用仅仅是唤醒一个睡眠在可被 中断优先级上的进程,那么执行wait调用的父进程被唤醒继续执行wait调用的后续操作,然后等待其他的子进程。