为了理解信号,先从最熟悉的场景说起:
(1): 用户输入命令,在Shell中启动一个前台进程。
(2): 用户按下Ctrl-C,这个键盘输入产生一个硬件中断
(3): 如果CPU当前正在执行这个进程的代码,则该进程的用户空间代码暂时停止运行,CPU从用户态切换到核心态处理硬件中断
(4):终端驱动程序将Ctrl-C解释为一个SIGINT信号,记在该进程的PCB中
(5):当某个时刻要从内核返回到该进程的用户空间代码继续执行之前,首先处理PCB中记录的信号,当发现有一个SIGINT信号时,而这个信号的默认处理动作是终止进程,
所以直接终止进程而不是返回到它的用户空间代码执行(提供信号处理函数另论)
注意的是:Shell中可以由一个前台进程组和多个后台进程组,像SIGINT这样的控制键产生的信号,终端驱动程序只会发送至前台进程,前台进程在运行到代码段的任意地方时,都可以接受到这样的信号,从而信号相对于进程来说是异步的。
信号产生的几种方式:
(1):用户按下终端键时候,例如Ctrl-C产生SIGINT信号
(2):硬件异常产生的信号:除数为0,,无效的内存引用
(3):进程调用kill函数可将信号发送给另一个进程或者是进程组。但是接受者和发送信号的所有者必须相同.或者是发送信号的进程的所有者为root
(4);当检查到某种软件条件已经产生,并将其通知有关进程时也产生信号。
信号是异步事件的例子,我们不能通过检查某个特定的变量来检查一个信号是否发生,原因很简单,信号在任何时候都有可能发生,我们总不能写一行用户代码,检测一下特定的变量吧,这样程序很是混乱,所以这个时候必须在信号产生的时候告诉内核应该怎样做。这样的话,信号处理函数signal(),感觉可以写在代码段的任何地方,这个函数的第一个参数是信号常量,第二个参数是信号的处理函数,这个函数的作用只是告诉内核当产生某个特定的信号时你应该怎么做,也就是说当代码段执行到这里的时候,会在PCB中进行信号处理函数指针的记录,而非真正的取处理这个信号,因为信号可能尚未产生,这样的话这个signal函数最好是写在main函数的开头,因为代码段的执行是在不同的进程的上下文之间不停的切换,如果系统很是繁忙的话,可能已经产生了某种信号,但是signal函数可能由于位置太靠后,而未做相应的记录,导致信号的处理不是我们自己定义的处理函数的结果。
信号处理的三种方式:
(1):忽略该信号,(2);捕捉信号,执行用户的信号处理函数 (3):执行系统默认动作。
注意:有两种信号不能忽略,SIGKILL与SIGSTOP,原因是向超级用户提供了进程终止或者是停止的可靠方法。这样的话,这两个信号也就不能被捕捉.
可以通过kill -l 查看系统的信号.