一、signal的介绍

  软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。在软件层次上是对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到来,事实上,进程也不知道信号到底什么时候到达。进程之间可以互相通过系统调用kill()发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件信号除了基本通知的功能外,还可以传递附加信息。收到信号的进程对各种信号有不同的处理方法。处理方法可以分成三类:

  • 第一种是类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数处理;
  • 第二种方法是忽略某个信号,对该信号不做任何处理,就像未发生过一样;
  • 第三种方法是,对该信号的处理保留系统的默认值,这种缺省操作,对大部分的信号的缺省操作是使得进程终止。进程通过调用signal来指定进程对某个信号的处理行为。

二、信号的分类

· 可靠信号与不可靠信号

   Linux信号机制基本上是从Unix系统中继承过来的。早期Unix系统中的信号机制比较简单和原始,信号值小于SIGRTMIN的信号都是不可靠信号。这就是"不可靠信号"的来源,它的主要问题是信号可能丢失。随着时间的发展,实践证明了有必要对信号的原始机制加以改进和扩充。由于原来定义的信号已有许多应用,不好再做改动,最终只好又新增加了一些信号,并在一开始就把它们定义为可靠信号,这些信号支持排队,不会丢失。我们可以使用 kill -l 命令查看当前系统支持的信号,需要注意的是不同的系统支持的信号是不一样的:

signal 技术架构 signal功能介绍_信号处理


   信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号,可靠信号克服了信号可能丢失的问题。对于目前linux的两个信号安装函数:signal()及sigaction()来说,它们都不能把SIGRTMIN以前的信号变成可靠信号(都不支持排队,仍有可能丢失,仍然是不可靠信号),而且对SIGRTMIN以后的信号都支持排队。这两个函数的最大区别在于,经过sigaction安装的信号都能传递信息给信号处理函数,而经过signal安装的信号不能向信号处理函数传递信息。对于信号发送函数来说也是一样的。

· 实时信号和非实时信号

  早期Unix系统只定义了32种信号,前32种信号已经有了预定义值,每个信号有了确定的用途及含义,并且每种信号都有各自的缺省动作。如按键盘的CTRL ^C时,会产生SIGINT信号,对该信号的默认反应就是进程终止。后32个信号表示实时信号,等同于前面阐述的可靠信号。这保证了发送的多个实时信号都被接收。

  非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。

  通过上文的了解,可以知道 kill -l 命令可以查看信号种类,下面我们就了解一下常用信号的作用

信号编号

信号名

信号说明

默认动作

2

SIGINT

Ctrl+C按键终止程序运行的信号

程序终止

4

SIGILL

非法的指令

程序终止

7

SIGBUS

运行非本CPU相关编译器编译的程序

程序终止

9

SIGKILL

强制杀死程序信号,任何程序都不可以捕捉该信号 程序终止,

不可被捕捉

10

SIGUSR1

用户自定义信号1

程序终止

11

SIGSEGV

段错误系统给程序发送的信号

程序终止

12

SIGUSR2

用户自定义信号2

程序终止

13

SIGPIPE

管道破裂信号

程序终止

14

SIGALRM

alarm()系统调用发送的信号

程序终止

15

SIGTERM

kill命令默认发送的信号,默认动作是终止信号

程序终止

17

SIGCHLD

子进程退出信号

忽略该信号

三、安装信号

(1)函数原型

信号的使用最好的方式还是: 查看man手册
Linux下有signal()和sigaction()两种信号安装的函数, 其中signal()函数的原型如下:

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
  • 第一个参数指定信号的值,
  • 第二个参数指定针对前面信号值的处理,可以忽略该信号(参数设为SIG_IGN);可以采用系统默认方式处理信号(参数设为SIG_DFL);也可以自己实现处理方式(参数指定一个函数地址)。

如果signal()调用成功,返回最后一次为安装信号signum而调用signal()时的handler值;失败则返回SIG_ERR。传递给信号处理例程的整数参数是信号值,这样可以使得一个信号处理例程处理多个信号。

(2)程序示例

1 #include <stdio.h>
 2 #include <string.h>
 3 #include <unistd.h>
 4 #include <signal.h>
 5 #include <stdlib.h>
 6 #include <execinfo.h>
 7
 8 int g_sigstop = 0;
 9
10 void signal_stop(int signum)
11 {
12 if( SIGTERM == signum )
13 {
14 printf("SIGTERM signal detected\n");
15 }
16 else if( SIGALRM == signum )
17 {
18 printf("SIGALRM signal detected\n");
19 g_sigstop = 1;
20 }
21 }
22
23 void signal_user(int signum)
24 {
25 if(SIGUSR1 == signum)
26 {
27 printf("SIGUSR1 signal detected\n");
28 }
29 else if(SIGUSR2 == signum)
30 {
31 printf("SIGUSR2 signal detected\n");
32 }
33
34 g_sigstop = 1;
35 }
36
37 void signal_code(int signum)
38 {
39 if(SIGBUS == signum)
40 {
41 printf("SIGBUS signal detected\n");
42 }
43 else if(SIGILL == signum)
44 {
45 printf("SIGILL signal detected\n");
46 }
47 else if(SIGSEGV == signum)
48 {
49 printf("SIGSEGV signal detected\n");
50 }
51
52 exit(-1);
53 }
54
55 int main(int argc, char **argv)
56 {
57 char *ptr=NULL;
58 struct sigaction sigact, sigign;
59 
60 /*+--------------------------------------+
61 *| Method1: Use signal() install signal |
62 *+--------------------------------------+*/
63 
64 signal(SIGTERM, signal_stop);
65 signal(SIGALRM, signal_stop);
66 
67 signal(SIGBUS, signal_code);
68 signal(SIGILL, signal_code);
69 signal(SIGSEGV, signal_code);
70 
71 /*+-----------------------------------------+
72 *| Method2: Use sigaction() install signal |
73 *+-----------------------------------------+*/
74 
75 /* Initialize the catch signal structure. */
76 sigemptyset(&sigact.sa_mask);
77 sigact.sa_flags = 0;
78 sigact.sa_handler = signal_user;
79 
80 /* Setup the ignore signal. */
81 sigemptyset(&sigign.sa_mask);
82 sigign.sa_flags = 0;
83 sigign.sa_handler = SIG_IGN;
84 
85 sigaction(SIGINT, &sigign, 0); /* ignore SIGINT signal by CTRL+C */
86 
87 sigaction(SIGUSR1, &sigact, 0); /* catch SIGUSR1 */
88 sigaction(SIGUSR2, &sigact, 0); /* catch SIGUSR1 */
89
90 
91 printf("Program start running for 20 seconds...\n");
92 alarm(20);
93 
94 while( !g_sigstop )
95 { 
96 ;
97 }
98 
99 printf("Program start stop running...\n");
100 
101 printf("Invalid pointer operator will raise SIGSEGV signal\n");
102 *ptr = 'h';
103 
104 return 0;
105 }

注意:kill信号真的是杀死信号吗?
  我们把 kill 理解为杀死一个进程,其实这样的描述是不准确的,kill 其实是作为一个信号,由其中一个进程发给另一个进程,而发送的信号就是终止信号