关键词:
本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】
前言:信号实际上是一种软中断,信号机制实际上是进程间通信的一种方式。状态改变、系统异常、系统状态的变化等等,这些是信号的来源。信号作为一个异步事件,对维护服务器稳定,避免异常终止有重要的作用。本文从常见的64中Linux信号出发,步步深入,详解信号事件。
目录
64种Linux信号
使用 kill -l 命令查看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
16) SIGSTKFLT 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
这些信号都定义在bits/signum.h头文件中:
/* Signals. */
#define SIGHUP 1 /* Hangup (POSIX). 终端连接断开信号*/
#define SIGINT 2 /* Interrupt (ANSI). 中断信号,终端中输入ctrl+c,可中断前台进程*/
#define SIGQUIT 3 /* Quit (POSIX). 退出信号,终端中输入ctrl+\\,可退出前台进程,同时产生core文件*/
#define SIGILL 4 /* Illegal instruction (ANSI). 非法指令信号,4.3BSD的abort函数产生该信号 */
#define SIGTRAP 5 /* Trace trap (POSIX). 调试信号,当在程序中设置断点后,该信号使得调试程序获得控制权*/
#define SIGABRT 6 /* Abort (ANSI). 程序异常终止信号,abort函数产生该信号 */
#define SIGIOT 6 /* IOT trap (4.2 BSD). 功能同SIGABRT*/
#define SIGBUS 7 /* BUS error (4.2 BSD). 程序访问不存在的内存区域时,产生该信号*/
#define SIGFPE 8 /* Floating-point exception (ANSI). 算术异常,如除以0*/
#define SIGKILL 9 /* Kill, unblockable (POSIX). 不能被忽略,非阻塞,可杀死任意一个运行中的进程*/
#define SIGUSR1 10 /* User-defined signal 1 (POSIX). 用户自定义1*/
#define SIGSEGV 11 /* Segmentation violation (ANSI). 当程序访问没有访问权限的内存区域,或者访问非可读的内存区域时,产生该信号,如数组越界 */
#define SIGUSR2 12 /* User-defined signal 2 (POSIX). 用户自定义2 */
#define SIGPIPE 13 /* Broken pipe (POSIX). 当管道读端已关闭,继续往管道中写,产生该信号 */
#define SIGALRM 14 /* Alarm clock (POSIX). alarm函数超时时产生该信号,默认动作是程序终止 */
#define SIGTERM 15 /* Termination (ANSI). 终止程序信号,命令kill默认使用该参数*/
#define SIGSTKFLT 16 /* Stack fault. */
#define SIGCLD SIGCHLD /* Same as SIGCHLD (System V). */
#define SIGCHLD 17 /* Child status has changed (POSIX). 子进程终止或停止时,产生该信号,默认被忽略*/
#define SIGCONT 18 /* Continue (POSIX). */
#define SIGSTOP 19 /* Stop, unblockable (POSIX). 停止一个作业控制进程*/
#define SIGTSTP 20 /* Keyboard stop (POSIX). ctrl+z产生该信号,改信号使得前台进程挂起*/
#define SIGTTIN 21 /* Background read from tty (POSIX). */
#define SIGTTOU 22 /* Background write to tty (POSIX). */
#define SIGURG 23 /* Urgent condition on socket (4.2 BSD). */
#define SIGXCPU 24 /* CPU limit exceeded (4.2 BSD). 进程超过了CPU软限制产生该信号*/
#define SIGXFSZ 25 /* File size limit exceeded (4.2 BSD). 进程超过了文件大小软限制产生该信号*/
#define SIGVTALRM 26 /* Virtual alarm clock (4.2 BSD). */
#define SIGPROF 27 /* Profiling alarm clock (4.2 BSD). */
#define SIGWINCH 28 /* Window size change (4.3 BSD, Sun). */
#define SIGPOLL SIGIO /* Pollable event occurred (System V). */
#define SIGIO 29 /* I/O now possible (4.2 BSD). */
#define SIGPWR 30 /* Power failure restart (System V). */
#define SIGSYS 31 /* Bad system call. */
#define SIGUNUSED 31
#define _NSIG 65 /* Biggest signal number + 1
(including real-time signals). */
#define SIGRTMIN (__libc_current_sigrtmin ())
#define SIGRTMAX (__libc_current_sigrtmax ())
取值 | 名称 | 解释 | 默认动作 |
---|---|---|---|
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 | 系统调用异常 |
当然,这64种信号我们不需要全部掌握,重点关注一下网络编程常用的SIGHUP、SIGPIPE、SIGURG、SIGALRM、SIGCHLD等。
信号来源
信号来源分为硬件类和软件类:
硬件方式
- 用户输入:比如在终端上按下组合键ctrl+C,产生SIGINT信号;
- 硬件异常:CPU检测到内存非法访问等异常,通知内核生成相应信号,并发送给发生事件的进程;
软件方式
通过系统调用,发送signal信号:kill(),raise(),sigqueue(),alarm(),setitimer(),abort()
- kernel,使用 kill_proc_info()等
- native,使用 kill() 或者raise()等
- java,使用 Procees.sendSignal()等
信号的发送
使用kill函数进行信号发送,函数原型如下:
#include <sys/types.h>
#include <sygnal.h>
int kill(ipd_t pid, int sig);
函数的含义是将sig信号发送给目标进程,目标进程由pid参数指定。
pid参数如下:
pid参数 | 含义 |
pid > 0 | 信号发送给PID为pid的进程 |
pid = 0 | 信号发送给本进程组内的其他进程 |
pid = -1 | 信号发送给除init进程之外的所有进程; 但发送者需要拥有对目标进程发送信号的权限 |
pid < -1 | 信号发送给组ID为-pid的进程组中的所有成员 |
如果sig信号等于0,则kill不发送任何信号,单数可以以此来监测目标进程是否存在。
信号的接受与处理
信号处理函数原型如下:
#include <bits/signal.h>
typedef void (*__sighandler_t) (int);
信号处理函数只带有一个整型参数,该参数用来指示信号类型。
除了用户自定义信号处理函数之外,还定义了信号两种其他处理方式:
#include <bits/signum.h>
#define SIG_DFL ((__sighandler_t))
#define SIG_IGN ((__sighandler_t))
SIG_DFL表示使用信号默认处理方式:结束进程、忽略信号、结束进程并生成核心转存储文件、暂停进程、继续进程
SIG_IGN表示忽略目标信号
中断系统调用
如果程序在执行处于阻塞状态的系统调用时接收到信号,并且为该信号设置了信号处理函数,则默认情况下系统调用将被中断,并将errno设置为EINTR。
信号安装
进程处理某个信号前,需要先在进程中安装此信号。安装过程主要是建立信号值和进程对相应信息值的动作。
信号安装函数
- signal():不支持信号传递信息,主要用于非实时信号安装;
- sigaction():支持信号传递信息,可用于所有信号安装;
signal系统调用
signal为一个信号设置处理函数:
#include <signal.h>
_sighandler_t signal(int sig, _sighandler_t _handler);
其中sig表示要捕获信号类型,_handler是函数指针,指定信号sig处理函数。
sigaction系统调用
相比于signal函数,sigaction调用更加健壮:
#include <signal.h>
int sigaction(int sig, const struct sigaction* act, struct sigaction* oact);
- sig表示要捕获信号类型
- act表示新的信号处理方式
- oact表示输出信号先前的处理方式
sigaction结构体:
- sa_handler:信号处理函数
- sa_mask:指定信号处理程序执行过程中需要阻塞的信号;
- sa_flags:标示位
- SA_RESTART:使被信号打断的syscall重新发起。
- SA_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD 信号。
- SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到SIGCHLD信号,这时子进程如果退出也不会成为僵 尸进程。
- SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。
- SA_RESETHAND:信号处理之后重新设置为默认的处理方式。
- SA_SIGINFO:使用sa_sigaction成员而不是sa_handler作为信号处理函数。
信号发送
- kill():用于向进程或进程组发送信号;
- sigqueue():只能向一个进程发送信号,不能像进程组发送信号;主要针对实时信号提出,与sigaction()组合使用,当然也支持非实时信号的发送;
- alarm():用于调用进程指定时间后发出SIGALARM信号;
- setitimer():设置定时器,计时达到后给进程发送SIGALRM信号,功能比alarm更强大;
- abort():向进程发送SIGABORT信号,默认进程会异常退出。
- raise():用于向进程自身发送信号;
信号集操作
- sigemptyset(sigset_t *set):信号集全部清0;
- sigfillset(sigset_t *set): 信号集全部置1,则信号集包含linux支持的64种信号;
- sigaddset(sigset_t *set, int signum):向信号集中加入signum信号;
- sigdelset(sigset_t *set, int signum):向信号集中删除signum信号;
- sigismember(const sigset_t *set, int signum):判定信号signum是否存在信号集中。
信号挂起
通过sigpending函数获取进程当前被挂起信号集:
#include <signal.h>
int sigpending(sigset_t* set);
参数set用于保存被挂起的信号集。
参考
- https://blog.csdn.net/zb1593496558/article/details/80280346?utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control
- http://gityuan.com/2015/12/20/signal/
- 《Linux高性能服务器编程》
- https://blog.csdn.net/ruglcc/article/details/86519357
手把手写c++服务器(36):手撕代码——高并发高qps技术基石之非阻塞recv万字长文(代码片段)
本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:没有什么东西是永恒,没有什么方案是万能,阻塞模式和非阻塞模式各有利弊。创建socket是默认阻塞的。但是在高并发多Q... 查看详情
手把手写c++服务器(26):常用i/o操作创建文件描述符
本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:通过上一篇文章(手把手写C++服务器(25):万物皆可文件之socketfd),Linux万物皆文件的一定深入人心。如何操作这些文件?I/O函数将震撼登场!第一... 查看详情
手把手写c++服务器(37):手撕代码——高并发多线程技术基石之异步connect万字长文(代码片段)
本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:connect创建的时候是默认阻塞模式的,但是现实情况里可能会因为网络差、中间代理服务器、网关等因素造成连接速度慢。此... 查看详情
手把手写c++服务器(27):五大文件描述符零拷贝控制总结(代码片段)
本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:前文《手把手写C++服务器(26):常用I/O操作、创建文件描述符》、《手把手写C++服务器(25):万物皆可文件之sock... 查看详情
手把手写c++服务器(35):手撕代码——高并发高qps技术基石之非阻塞send万字长文(代码片段)
本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:创建socket是默认阻塞的。但是在高并发多QPS的场景中,阻塞模式会极大程度上影响并发性,使之并发名存实亡。而send函... 查看详情
手把手写c++服务器:专栏文章-汇总导航更新中
手把手写C++服务器(1):网络编程常见误区手把手写C++服务器(2):C/C++编译链接模型、函数重载隐患、头文件使用规范手把手写C++服务器(3):C++编译常见问题、编译优化方法、C++库发... 查看详情
手把手写c++服务器(38):面试必背!linux网络socket编程必会十问!(代码片段)
本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】目录1、说一下客户端和服务端socket建立连接和关闭连接的过程2、如何将一个socket设置成非阻塞模式3、什么是socket三大属性?4、阻塞模... 查看详情
手把手写c++服务器(33):linux常用命令合集
本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:服务端编程的过程当中,各种常用的命令行也会大量使用;熟悉常用Linux命令不仅仅是运维的基本要求,也是一个主程的基本门槛。这里... 查看详情
手把手写c++服务器(28):手撕cgi通用网关接口服务器代码(代码片段)
本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:前文《手把手写C++服务器(26):常用I/O操作、创建文件描述符》《手把手写C++服务器(27):五大文件描述符零拷... 查看详情
手把手写c++服务器(29):手撕echo回射服务器代码(代码片段)
本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:上一讲《手把手写C++服务器(28):手撕CGI通用网关接口服务器代码》讲解了如何利用复制文件描述符dup重定位标准输出... 查看详情
手把手写c++服务器(30):手撕代码——基于tcp/ip的抛弃服务discard(代码片段)
本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:前面两讲讲了echo服务器和CGI网关服务器《手把手写C++服务器(29):手撕echo回射服务器代码》《手把手写C++服务器(2... 查看详情
手把手写c++服务器(31):服务器性能提升关键——io复用技术两万字长文
本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:Linux中素有“万物皆文件,一切皆IO”的说法。前面几讲手撕了CGI网关服务器、echo回显服务器、discard服务的代码,但是这几个一次只能监听... 查看详情
手把手写c++服务器(21):linuxsocket网络编程入门基础(代码片段)
本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】前言:刚开始写C++服务器的时候,我们进行网络编程肯定是使用socketAPI,等熟练之后,会根据我们自己的需要,封装... 查看详情
手把手写c++服务器(22):linuxsocket网络编程进阶第一弹(代码片段)
前言:前面一篇文章手把手写C++服务器(21):Linuxsocket网络编程入门基础,讲解了如何建立socket连接、如何转换/使用socket地址、如何绑定/监听/发起/接受/断开/终止/关闭连接。socket博大精深,进阶会多写几弹&... 查看详情
手把手写c++服务器(22):linuxsocket网络编程进阶第一弹(代码片段)
前言:前面一篇文章手把手写C++服务器(21):Linuxsocket网络编程入门基础,讲解了如何建立socket连接、如何转换/使用socket地址、如何绑定/监听/发起/接受/断开/终止/关闭连接。socket博大精深,进阶会多写几弹&... 查看详情
手把手写c++服务器(23):必知必会!操作系统面试十连问
前言:系列文章手把手写C++服务器(17):自测!TCP协议面试经典十连问受到了大家的收藏和点赞,谢谢读者的支持。操作系统是后端开发工程师的童子功,掌握常见的操作系统基础知识不仅有助于我们日... 查看详情
手把手写c++服务器(34):高并发高吞吐io秘密武器——epoll池化技术两万字长文(代码片段)
本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:前文手把手写C++服务器(31):服务器性能提升关键——IO复用技术【两万字长文】介绍了IO复用技术,其中重点比较了... 查看详情
手把手写c++服务器(17):自测!tcp协议面试经典十连问(代码片段)
前言:前面一篇文章《手把手写C++服务器(15):网络编程入门第一个TCP项目》介绍了一个简单入门级的TCP项目,这一篇文章重点讲一讲面试常见的TCP协议相关的十个问题,都是后端开发程序员必知必会的经典... 查看详情