本文已参与「新人创作礼」活动,一起开启掘金创作之路。
认识
什么是信号? 信号:软件(进程)中断,通知事件的发生。
信号有不同的种类,每个信号对应不同事件。
-
信号的生命周期:从产生,到处理。
-
Linux下信号的种类:(62种)
1~31号继承于Unix系统,每个信号都有各自对应的事件。(非可靠信号 / 非实时信号)。
34~64后续添加的信号,没有特定事件。(可靠信号 / 实时信号)。
非可靠 / 可靠:信号有可能会
丢失/ 有多少信号就要处理多少次 实时 / 非实时:信号立刻 / 选择合适时机处理。
指令kill -l:查看所有信号信息
生命周期
- 产生
- 注册(到进程中)
- 注销
- 处理
- (阻塞)暂时不处理信号。
本章节主要讲前4节。
信号的产生
硬件产生
组合键(通过硬件产生软件信号)
Ctrl+c(2号SIGINT中断信号)Ctrl+|(3号SIGQUIT退出信号)Ctrl+z(19号SIGSTOP停止信号)
软件产生
kill -<signum> [pid]命令
- 主动:函数
kill()(man 2)
int kill(pid_t pid,int sig);
给指定进程发送指定信号,使用宏定义数字,如果要使用英文缩写要包含头文件<signal.h>。
宏定义数字查看:文件处于/usr/include/bits/signum.h中
kill实际上是15号信号SIGTERM。
kill -9实际上是9号信号SIGKILL。
raise()
int raise(int sig);
发送给调用进程/线程(自己)发送指定信号。(使用函数需要包含头文件<signal.h>)
abort()
void abort(void);
相当于调用了raise接口,给调用进程发送SIGABRT信号退出(使用函数需要包含头文件<stdlib.h>)
alarm()
unsigned int alarm(unsigned int seconds);
定时器,seconds秒之后给调用进程发送SIGALRM信号,返回上一个定时器剩余的时间或0(如果没有定时器)。
alarm(0):参数设0表示取消定时器,并且返回剩余时间。
- 被动:程序异常
core dump:程序异常退出时保存程序运行信息,用于事后调试,默认关闭(因为占用磁盘资源,安全性考虑(例如对段错误的数据进行分析获得用户密码))。
ulimit -a:查看core dump是否开启。
ulimit -c + [num]设置核心转储文件大小,开启核心转储(num设置为unlimited表示不限制大小)
核心转储文件core.之后的数字意为pid。
gdb加载程序进行调试流程:
gdb + [.c程序名]core-file [corepid]bt查看调用栈 执行结束即可查看到在那个接口发生的错误。
kill()只负责发送信号,进程在处理这个信号时,kill还没有返回,函数调用栈就到此为止了。错误并不是函数产生的,而是在函数期间收到异常信号而退出的。
信号的注册
这里讲的注册是信号在进程中的注册。
sigset_t结构体:
# define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int)))
typedef struct {
unsigned long int __val[_SIGSET_NWORDS];
}__sigset_t;
其中是一个数组,对于一个进程来说,判断是否收到了某个信号,就代表进程有没有在数组中做标记。
实际上是在进程的PCB中注册标记,需要待处理。这些标记在这个结构体中记录。通过位图实现。
信号的种类有62类,按数字来标记的话是0~64,在信号的集合__sigset_t中添加一个信号,实际上是将这个信号的编号所定义的位进行置1操作。
这个数组的长度为1024 * (8 * sizeof(unsigned long int)),再加上这个数据的类型unsigned long int,应该是8*1024/8*8位,最终计算下来有1024个比特位。
-
非可靠信号的注册: 判断
pending(信号集合位图)相应位是否为1,若为0,为信号组织sigqueue节点添加到链表中,并且pending位图置1。若为1,说明信号已经被注册过,还没有被处理,第二个相同的信号等于被丢弃。 -
可靠信号的注册: 不管位图是否为
1,组织节点,添加到链表中,并且位图置1,信号没有被丢弃。
信号的注销
非可靠信号(1~31号):因为非可靠信号的信号结点只有一个,因此删除结点,位图直接置0。
可靠信号(34~64号):因为可靠信号的结点有可能会有多个,若还有相同信号结点,则位图依然置1,否则置0。
信号的处理
signal(int signum, sighandler_t handler); //回调函数,修改信号的处理方式
- 默认处理:
SIG_DFL(default) - 忽略处理:
SIG_IGN(ignore) 如果形如signal(2,SIG_IGN);,运行后不能通过2号信号停止,但可以通过3号信号进行停止。 - 自定义处理