信号及信号来源
信号(signal),全称软中断信号,其本质是软件层次上对中断机制的一种模拟,用于提醒进程,某件事情已经发生。Unix早期版本中就提供了信号模型,但当时的信号模型并不可靠,信号容易丢失,之后加州伯克利大学和美国的AT&T公司对信号模型做了改进,添加了可靠信号机制,POSIX标准又对可靠信号的功能和应用接口进行了标准化,逐渐发展出了如今Linux系统中使用的信号。但不同Linux版本中的信号仍有所不同,读者可以使用kill –l命令查看系统中的信号,CentOS 6.8版本的Linux系统中的信号如图1所示。
图1 Linux系统中的信号
信号的编号从1开始,其中1~31号信号为常规信号,也就是早期信号模型中的不可靠信号;34~64号信号为实时信号,用于计算机底层开发中的信号,是经POSIX标准统一过接口和功能的可靠信号。本章重点讲解的是常规信号中的部分常用信号。
信号被应用于进程间通信,但信号实际并不由进程发送,在遇到某种情况时,内核会发送某个信号到某个进程。通常产生信号的情况有以下五种:
(1)用户在终端输入某些组合按键时,终端驱动程序会通知内核产生一个信号,之后内核将该信号发送到相应进程。这些信号的功能通常为停止或终止正在占用终端的进程,如使用Ctrl+C时会发送2号信号SIGINT、使用Ctrl+\会发送3号信号SIGQUIT中断前台进程;使用Ctrl+Z时会发送20号信号SIGTSTP到正在占用终端的进程将其挂起。
(2)当硬件检测到异常时,如段错误、除0(浮点数除外)、总线错误等异常等,内核会产生信号并发送信号到正在运行的程序。
(3)满足某种软件条件时内核也会产生信号,比如alarm计时器计时结束时会发送26号信号SIGALRM到正在运行的进程。
(4)用户进程可以在程序中通过调用系统调用kill、raise、abort等,发送指定信号给指定进程或进程组。
(5)用户在shell命令行,可以使用kill命令向指定进程发送信号。在第六章中使用kill命令杀死进程,其实质就是发送9号信号SIGKILL到指定进程。
信号的产生是一个异步事件,从信号产生到信号递达进程需要一定时间,而在这个过程中,可能会因为一些原因,导致信号无法成功递达进程。Linux系统中的信号可能会处于几个状态,分别为:发送状态、阻塞状态、未决状态、递达状态和处理状态。
● 发送状态:当某种情况驱使内核发送信号时,信号会有一个短暂的发送状态;
● 阻塞状态:由于某种原因,发送的信号无法被传递,将处于阻塞状态;
● 未决状态:发送的信号被阻塞,无法到达进程,内核就会将该信号的状态设置为未决;
● 递达状态:若信号发送后没有阻塞,信号就会被成功传递并到达进程,此时为递达状态;
● 处理状态:信号被递达后会被立刻处理,此时信号处于处理状态。
信号递达进程后才可能被处理,信号的处理方式有三种:忽略、捕捉和执行默认操作。
(1)忽略:大多数信号都可以被忽略,但9号信号SIGKILL和19号信号SIGSTOP是超级用户杀死进程的可靠方法,不能被忽略。
(2)捕获:对信号做捕获处理时,进程通常需要先为该信号设置信号响应函数,这是一个回调函数,当指定信号产生时,内核会为该进程调用并执行对应的信号响应函数。9号信号SIGKILL和19号信号SIGSTOP同样不能被捕获。
(3)执行默认动作:系统为每个信号设置了一些默认动作,当信号递达,进程又未设置信号的响应函数时,系统会对进程执行信号的默认动作。
信号的默认动作有五个,分别为Term、Ign、Core、Stop和Cont,每个动作代表的含义如下:
● Term:终止进程;
● Ign:忽略信号;
● Core:终止进程,并生成Core文件;
● Stop:暂停进程;
● Cont:继续运行进程。
信号会在不同的事件发生时被发送给对应进程,每个进程对应的事件,以及进程接收到信号后的默认动作如表1所示。
表1 常规信号属性表
编号 | 名称 | 事件 | 默认动作 |
---|---|---|---|
1) | SIGHUP | 当用户退出shell时,由该shell启动的所有进程将收到这个信号。 | Term |
2) | SIGINT | 当用户按下<Ctrl+C>组合键时,用户终端向正在运行中的由该终端启动的程序发出此信号。 | Term |
3) | SIGQUIT | 当用户按下<Ctrl+>组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出此信号。 | Term |
4) | SIGILL | CPU检测到某进程执行了非法指令时。 | Core |
5) | SIGTRAP | 该信号由断点指令或其它trap指令产生。 | Core |
6) | SIGABRT | 调用abort函数时产生该信号。 | Core |
7) | SIGBUS | 非法访问内存地址。 | Core |
8) | SIGFPE | 在发生致命的运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为0等所有的算法错误。 | Core |
9) | SIGKILL | 无条件终止进程。本信号不能被忽略、处理和阻塞。 | Term |
10) | SIGUSE1 | 用户定义的信号,程序员可以在程序中定义并使用该信号。 | Term |
11) | SIGSEGV | 指示进程进行了无效内存访问。 | Core |
12) | SIGUSR2 | 另外一个用户自定义信号,程序员可以在程序中定义并使用该信号。 | Term |
13) | SIGPIPE | Broken pipe向一个没有读端的管道写数据。 | Term |
14) | SIGALRM | 定时器超时,超时的时间由系统调用alarm设置。 | Term |
15) | SIGTERM | 程序结束信号,与SIGKILL不同的是,该信号可以被阻塞和终止,通常用来表示程序正常退出。执行shell命令Kill时,缺省产生这个信号。 | Term |
16) | SIGSTKFLT | Linux早期版本出现的信号,现仍保留向后兼容。 | Term |
17) | SIGCHLD | 子进程结束时,父进程会收到这个信号。 | Ign |
18) | SIGCONT | 如果进程已停止,则使其继续运行。 | Cont/Ign |
19) | SIGSTOP | 停止进程的执行。该信号不能被忽略,处理和阻塞。 | Stop |
20) | SIGTSTP | 按下<ctrl+z>组合键时发出这个信号,停止终端交互进程的运行。 | Stop |
21) | SIGTTIN | 后台进程读终端控制台。 | Stop |
22) | SIGTTOU | 该信号类似于SIGTTIN,在后台进程要向终端输出数据时发生。 | Stop |
23) | SIGURG | 套接字上有紧急数据时,向当前正在运行的进程发出些信号,报告有紧急数据到达。如网络带外数据到达时。 | Ign |
24) | SIGXCPU | 进程执行时间超过了分配给该进程的CPU时间,系统产生该信号并发送给该进程。 | Term |
25) | SIGXFSZ | 超过文件的最大长度设置。 | Term |
26) | SIGVTALRM | 虚拟时钟超时时产生该信号。类似于SIGALRM,但是该信号只计算该进程占用CPU的使用时间。 | Term |
27) | SGIPROF | 类似于SIGVTALRM,它不公包括该进程占用CPU时间还包括执行系统调用时间。 | Term |
28) | SIGWINCH | 窗口变化大小时发出。 | Ign |
29) | SIGIO | 此信号向进程指示发出了一个异步IO事件。 | Ign |
30) | SIGPWR | 关机。 | Term |
31) | SIGSYS | 无效的系统调用。 | Core |