Linux Signal

*爱你&永不变心* 提交于 2020-03-04 08:07:07

哪些情况会引发信号


1.键盘事件  ctrl +c  ctrl +\
2.非法内存  如果内存管理出错,系统就会发送一个信号进行处理
3.硬件故障  同样的,硬件出现故障系统也会产生一个信号
4.环境切换  比如说从用户态切换到其他态,状态的改变也会发送一个信号,这个信号会告知给系统

 

进程表的表项中有一个软中断信号域,进程对不同的信号可以同时保留,但对于同一个信号,进程并不知道在处理之前来过多少个。 

 

内核给一个进程发送软中断信号的方法,是在进程所在的进程表项的信号域设置对应于该信号的位。这里要补充的是,如果信号发送给一个正在睡眠的进程,那么要看 该进程进入睡眠的优先级,如果进程睡眠在可被中断的优先级上,则唤醒进程;否则仅设置进程表中信号域相应的位,而不唤醒进程。这一点比较重要,因为进程检 查是否收到信号的时机是:一个进程在即将从内核态返回到用户态时;或者,在一个进程要进入或离开一个适当的低调度优先级睡眠状态时。

 

运行如下命令,可看到Linux支持的信号列表:

$ kill -l
1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL
 5) SIGTRAP      6) SIGABRT      7) SIGBUS       8) SIGFPE
 9) SIGKILL     10) SIGUSR1     11) SIGSEGV     12) SIGUSR2
13) SIGPIPE     14) SIGALRM     15) SIGTERM     17) SIGCHLD
18) SIGCONT     19) SIGSTOP     20) SIGTSTP     21) SIGTTIN
22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO
30) SIGPWR      31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1
36) SIGRTMIN+2  37) SIGRTMIN+3  38) SIGRTMIN+4  39) SIGRTMIN+5
40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8  43) SIGRTMIN+9
44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13
52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9
56) SIGRTMAX-8  57) SIGRTMAX-7  58) SIGRTMAX-6  59) SIGRTMAX-5
60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2  63) SIGRTMAX-1
64) SIGRTMAX

编号1 ~ 31的信号为传统UNIX支持的信号,是不可靠信号(非实时的),编号为32 ~ 63的信号是后来扩充的,称做可靠信号(实时信号)。不可靠信号和可靠信号的区别在于前者不支持排队,可能会造成信号丢失,而后者不会。

 

进程收到信号三种处理方式
1 默认:如果是系统默认的话,那就会终止这个进程,SIGILL,SIGTRAP不能恢复为默认动作
2 忽略 :不作处理  SIGKILL  SIGSTOP 不能忽略
3 捕获并处理 :当信号来了,执行我们自己写的代码(捕获信号这个动作是需要我们完成的)  SIGKILL SIGSTOP 不能捕获

 

上面提到的几个特殊的信号:

SIGSTOP 停止(stopped)进程的执行. 注意它和terminate以及interrupt的区别:该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略.

SIGKILL 用来立即结束程序的运行. 本信号不能被阻塞、处理和忽略。如果管理员发现某个进程终止不了,可尝试发送这个信号。

SIGILL 执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出时也有可能产生这个信号。

SIGTRAP 由断点指令或其它trap指令产生. 由debugger使用。

 

系统调用

下面讲解两个比较重要的系统调用

 

signal 系统调用 
系统调用signal是进程用来设定某个信号的处理方法,系统调用kill是用来发送信号给指定进程的。

系统调用signal用来设定某个信号的处理方法。该调用声明的格式如下: 
void (*signal(int signum, void (*handler)(int)))(int); 
需要引入以下头文件: 
#include <signal.h> 

参数signum指出要设置处理方法的信号。第二个参数handler是一个处理函数,或者是 
SIG_IGN:忽略参数signum所指的信号。 
SIG_DFL:恢复参数signum所指信号的处理方法为默认值。 

信号处理函数以及调用的例子:

#include <signal.h> 
#include <unistd.h> 
#include <stdio.h> 
void sigroutine(int dunno) { /* 信号处理例程,其中dunno将会得到信号的值 */ 
switch (dunno) { 
case 1: 
printf("Get a signal -- SIGHUP "); 
break; 
case 2: 
printf("Get a signal -- SIGINT "); 
break; 
case 3: 
printf("Get a signal -- SIGQUIT "); 
break; 
} 
return; 
} 

int main() { 
printf("process id is %d ",getpid()); 
signal(SIGHUP, sigroutine); //* 下面设置三个信号的处理方法 
signal(SIGINT, sigroutine); 
signal(SIGQUIT, sigroutine); 
for (;;) ; 
} 

这么写的目的是在一个信号处理的实现里面处理多个信号

运行的结果:

Get a signal -SIGINT //按下Ctrl-C得到的结果 

或者: kill -HUP 463 //向进程发送SIGHUP信号 
localhost:~$ Get a signal – SIGHUP 

其中process id is 463  

kill系统调用

用来向任何进程或进程组发送任何信号。

int kill(pid_t pid, int sig); 

pid 发送给
>0 将信号sig发送到进程号为pid的进程
=0 信号sig将发送给当前进程所属进程组里的所有进程
=-1 信号sig将发送给除了进程1和自身以外的所有进程
<-1 信号sig将发送给属于进程组-pid的所有进程

如果参数sig为0,将不发送信号。该调用执行成功时,返回值为0;错误时,返回-1,并设置相应 的错误代码errno。

 

参考文章:

https://www.cnblogs.com/taobataoma/archive/2007/08/30/875743.html

https://cloud.tencent.com/developer/article/1433358

https://blog.csdn.net/zb1593496558/article/details/80280346

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!