一、linux信号是什么
- 基本概念
信号是事件发生时对进程的通知机制,也就是所谓的软件中断。信号和硬件的中断类似,是软件层对中断机制的模拟,在多数情况下是无法预测信号产生的时间,所以软件层提供了一种处理异步事件的方法。
二、 信号来源
信号的来源分为硬件来源和软件来源。
- 硬件来源:
- 硬件发生异常,即硬件检测到错误条件并通知内核,随即再由内核发送相应的信号给相关进程,如除数为0、无效的内存引用等。
- 用户按终端键,引起终端产生的信号(比如Ctrl + C键产生SIGINT)。
- 软件来源:
- 用户通过指令杀死,如kill指令。
- 发生软件事件, 如程序执行raise, alarm、setitimer、sigqueue等函数。
三、 信号处理
信号通常是发送给对应的进程,当信号到达后,该进程需要做出相应的处理措施,通常进程会视具体信号执行相应的操作,有三种操作方式。
- 忽略信号:
信号到达后、直接忽略,就好像是没有出该信号,信号对该进程不会产生任何影响。事实上,大多数信号都可以使用这种方式进行处理,但有两种信号却决不能被忽略,分别是SIGKILL 和 SIGSTOP。
- 捕获信号:
当信号到达进程后,执行signal()绑定好的信号处理函数。
- 执行系统默认操作:
进程不对该信号事件作出处理,而是交由系统进行处理,每一种信号都会有其对应的系统默认的处理方式。
四、常见信号
在linux系统中通过kill -l
命令可以查看到相应的信号。信号编号是从 1 开始,不存在编号为 0 的信号,事实上 kill()函数对信号编号 0 有着特殊的应用。

注意:括号" ) "前面的数字对应该信号的编号,编号 1~31 所对应的是不可靠信号,编号 34~64 对应的是可靠信号,从图中可知,可靠信号并没有一个具体对应的名字,而是使用了 SIGRTMIN+N 或 SIGRTMAXN 的方式来表示。其中32和33空缺。
不可靠信号表
值 |
名称 |
解释 |
默认动作 |
1 |
SIGHUP |
挂起 |
|
2 |
SIGINT |
中断 |
|
3 |
SIGQUIT |
退出 |
|
4 |
SIGILL |
非法指令 |
|
5 |
SIGTRAP |
断点或陷阱指令 |
|
6 |
SIGABRT |
abort发出的信号 |
|
7 |
SIGBUS |
非法内存访问 |
|
8 |
SIGFPE |
浮点异常 |
|
9 |
SIGKILL |
kill信号 |
不能被忽略、处理和阻塞 |
10 |
SIGUSR1 |
用户信号1 |
|
11 |
SIGSEGV |
无效内存访问 |
|
12 |
SIGUSR2 |
用户信号2 |
|
13 |
SIGPIPE |
管道破损,没有读端的管道写数据 |
|
14 |
SIGALRM |
alarm发出的信号 |
|
15 |
SIGTERM |
终止信号 |
|
16 |
SIGSTKFLT |
栈溢出 |
|
17 |
SIGCHLD |
子进程退出 |
默认忽略 |
18 |
SIGCONT |
进程继续 |
|
19 |
SIGSTOP |
进程停止 |
不能被忽略、处理和阻塞 |
20 |
SIGTSTP |
进程停止 |
|
21 |
SIGTTIN |
进程停止,后台进程从终端读数据时 |
|
22 |
SIGTTOU |
进程停止,后台进程想终端写数据时 |
|
23 |
SIGURG |
I/O有紧急数据到达当前进程 |
默认忽略 |
24 |
SIGXCPU |
进程的CPU时间片到期 |
|
25 |
SIGXFSZ |
文件大小的超出上限 |
|
26 |
SIGVTALRM |
虚拟时钟超时 |
|
27 |
SIGPROF |
profile时钟超时 |
|
28 |
SIGWINCH |
窗口大小改变 |
默认忽略 |
29 |
SIGIO |
I/O相关 |
|
30 |
SIGPWR |
关机 |
默认忽略 |
31 |
SIGSYS |
系统调用异常 |
|
五、信号处理
- signal()
"signal.h"信号处理库提供了signal函数,用来捕获突发事件。以下是 signal() 函数的语法ads。
| typedef void (*sighandler_t)(int); |
| sighandler_t signal(int signum, sighandler_t handler); |
- signum:可使用信号名(宏)或信号的数字编号,建议使用信号名。
- handler:参数 handler 既可以设置为用户自定义的函数,也可以设置为 SIG_IGN 或 SIG_DFL,SIG_IGN 表示此进程需要忽略该信号,SIG_DFL 则表示设置为系统默认操作。
- raise()
有时进程需要向自身发送信号,raise()函数可用于实现这一要求.
- sigaction()
除了signal()之外,sigaction()系统调用是设置信号处理方式的另一选择,虽然 signal()函数简单好用,而 sigaction()更为复杂,但作为回报,sigaction()也更具灵活性以及移植性。
| int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); |
- signum:需要设置的信号,除了 SIGKILL 信号和 SIGSTOP 信号之外的任何信号。
- act:参数 act 不为 NULL,则表示需要为信号设置新的处理方式;如果参数 act 为 NULL,则表示无需改变信号当前的处理方式
- oldact:参数oldact 不为 NULL,则会将信号之前的处理方式等信息通过参数 oldact 返回出来;如果无意获取此类信息,那么可将该参数设置为 NULL。
- 返回值:成功返回 0;失败将返回-1,并设置 errno。
struct sigaction 结构体
| struct sigaction { |
| void (*sa_handler)(int); |
| void (*sa_sigaction)(int, siginfo_t *, void *); |
| sigset_t sa_mask; |
| int sa_flags; |
| void (*sa_restorer)(void); |
| }; |
- sa_handler:指定信号处理函数,与 signal()函数的 handler 参数相同。
- sa_sigaction:也用于指定信号处理函数,这是一个替代的信号处理函数。
- sa_mask:参数 sa_mask 定义了一组信号。
- sa_restorer:该成员已过时,不要再使用了。
- sa_flags:参数 sa_flags 指定了一组标志,这些标志用于控制信号的处理过程。
- kill()
kill()系统调用可将信号发送给指定的进程或进程组中的每一个进程。
| int kill(pid_t pid, int sig); |
- pid:参数 pid 为正数的情况下,用于指定接收此信号的进程 pid。
- sig:参数 sig 指定需要发送的信号,也可设置为 0,如果参数 sig 设置为 0 则表示不发送信号,但任执行错误检查,这通常可用于检查参数 pid 指定的进程是否存在。
- 返回值:成功返回 0;失败将返回-1,并设置 errno。
- alarm()
使用 alarm()函数可以设置一个定时器(闹钟),当定时器定时时间到时,内核会向进程发送 SIGALRM信号。
| unsigned int alarm(unsigned int seconds); |
- seconds:设置定时时间,以秒为单位;如果参数 seconds 等于 0,则表示取消之前设置的 alarm 闹钟。
- 返回值:如果在调用 alarm()时,之前已经为该进程设置了 alarm 闹钟还没有超时,则该闹钟的剩余值作为本次 alarm()函数调用的返回值,之前设置的闹钟则被新的替代;否则返回 0。
- pause()
pause()系统调用可以使得进程暂停运行、进入休眠状态,直到进程捕获到一个信号为止,只有执行了信号处理函数并从其返回时,pause()才返回,在这种情况下,pause()返回-1,并且将 errno 设置为EINTR。
- 使用案例
demo1
| #include <stdio.h> |
| #include <signal.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| |
| void signal_handler(int signum) |
| { |
| printf("Interrupt signal (%d) received. \n", signum); |
| switch(signum){ |
| case SIGINT: |
| printf("ctrl + c \n"); |
| exit(signum); |
| break; |
| case SIGQUIT: |
| printf("ctrl + \\ \n"); |
| exit(signum); |
| |
| } |
| } |
| |
| int main(int argc, char *argv[]) { |
| int i = 0; |
| |
| |
| signal(SIGINT, signal_handler); |
| signal(SIGQUIT, signal_handler); |
| |
| while(1) |
| { |
| printf("Going to sleep....\n"); |
| if (i > 5){ |
| raise( SIGINT); |
| } |
| sleep(1); |
| i++; |
| } |
| |
| return 0; |
| } |
| |
参考文献
Linux信号(signal)机制:http://gityuan.com/2015/12/20/signal/
linux 信号及处理过程详解:https://blog.csdn.net/u010765526/article/details/80085895
linux kill信号详解:https://www.cnblogs.com/gcb-1991/p/6922694.html
《正点原子应用编程指南》
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步