Unix中的signal用于通知进程中发生了异步事件。用户可以通过kill系统调用发送一个信号,kernel自己内部也可以发送信号给一个进程。
进程对信号可以有三种处理方式:忽略,处理和默认(exit)。
为了发送一个信号给一个进程,内核设置相对应于信号的bit位在进程的process table entry中,例如如果进程收到一个kill signal,它将设置相应的bit位在process table signal field,但是进程不能得知它接收了多少次这个信号。如果一个进程睡眠在一个可以被中断的级别上,内核将会唤醒这个睡眠的进程,进程的发送者至此完成任务。
内核仅当一个进程从kernel mode返回user mode时才处理一个信号。如果一个进程在user mode运行,且内核正在处理“一个发送信号给这个进程”的中断,内核将会在它返回user mode之前识别并且处理这个信号。
进程可以通过signal(signmu,handler);系统调用设置对信号的处理方式。在进程的u area中包含了一个signal-hanlder field,对应每个signal num和相应的处理函数。
当处理一个信号时,内核确定信号的类型,并在process table entry中设置该信号的bit位。如果该信号的处理方式被设置为default,内核将会在exit之前为此进程产生一个" core"。但是内核不会为产生core如果没有一个程序错误发生,例如信号SIG_INT和hang信号。但是quit信号仍然产生一个core,即使它在进程运行环境外产生,这通常是用户结束一个无限循环等。
当进程接收到一个信号后,在返回user mode之前,内核会做一些事:
1,保存的register context为返回user process。
2,清除在进程的u area中的signal handler field,设置它为default状态(如果设置了“忽略”状态,内核并不清除之前的“忽略”状态,下次发生这个信号时继续忽略)。
3,内核创建一个新的stack frame在用户stack,调用信号处理函数。
4,恢复1中保存的register context,将PC指向signal-catcher的地址(信号发生时)。
上述描述过程可能存在以下“异常”:
1,当进程返回到user mode之前,由于清除了u area中的signal handler field,只有再次调用signal系统调用时,才会再次扑捉到这个siganl,这是就可能存在一个竞争条件。当再次调用signal之前,相同型号又发生了,这是进程就会exit(忽略情况除外)。(参看Unix操作系统设计P207)
2,当一个进程sleep在可被中断的系统调用时,信号会造成longjmp调出睡眠,返回user mode并调用信号处理函数。当从信号处理函数返回时,进程看起来像从系统调用返回并且带有error表明此系统调用被中断了。
3,当一个进程sleep在不可被中断的系统调用时,进程将被唤醒,但是不做longjmp。即只有内核唤醒睡眠进程并运行它时,才发现忽略了此信号。