Linux之信号(上)

89 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

认识

什么是信号? 信号:软件(进程)中断,通知事件的发生。

信号有不同的种类,每个信号对应不同事件。

  • 信号的生命周期:从产生,到处理

  • Linux下信号的种类:(62种) 1~31号继承于Unix系统,每个信号都有各自对应的事件。(非可靠信号 / 非实时信号)。

34~64后续添加的信号,没有特定事件。(可靠信号 / 实时信号)。

非可靠 / 可靠:信号有可能会丢失 / 有多少信号就要处理多少次 实时 / 非实时:信号立刻 / 选择合适时机处理

指令kill -l:查看所有信号信息


生命周期

  1. 产生
  2. 注册(到进程中)
  3. 注销
  4. 处理
  5. (阻塞)暂时不处理信号。

本章节主要讲前4节。


信号的产生

硬件产生

组合键(通过硬件产生软件信号)

  • Ctrl+c(2号SIGINT中断信号)
  • Ctrl+|(3号SIGQUIT退出信号)
  • Ctrl+z(19号SIGSTOP停止信号)

软件产生

kill -<signum> [pid]命令

  • 主动:函数
  1. kill()(man 2)
int kill(pid_t pid,int sig);

给指定进程发送指定信号,使用宏定义数字,如果要使用英文缩写要包含头文件<signal.h>

宏定义数字查看:文件处于/usr/include/bits/signum.h

kill实际上是15号信号SIGTERM

kill -9实际上是9号信号SIGKILL

  1. raise()
int raise(int sig);

发送给调用进程/线程(自己)发送指定信号。(使用函数需要包含头文件<signal.h>

  1. abort()
void abort(void);

相当于调用了raise接口,给调用进程发送SIGABRT信号退出(使用函数需要包含头文件<stdlib.h>

  1. 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加载程序进行调试流程:

  1. gdb + [.c程序名]
  2. core-file [corepid]
  3. 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个比特位

  1. 非可靠信号的注册: 判断pending(信号集合位图)相应位是否为1,若为0,为信号组织sigqueue节点添加到链表中,并且pending位图置1。若为1,说明信号已经被注册过,还没有被处理,第二个相同的信号等于被丢弃。

  2. 可靠信号的注册: 不管位图是否为1,组织节点,添加到链表中,并且位图置1,信号没有被丢弃。


信号的注销

非可靠信号(1~31号):因为非可靠信号的信号结点只有一个,因此删除结点,位图直接置0

可靠信号(34~64号):因为可靠信号的结点有可能会有多个,若还有相同信号结点,则位图依然置1,否则置0


信号的处理

signal(int signum, sighandler_t handler);	//回调函数,修改信号的处理方式
  1. 默认处理:SIG_DFL(default)
  2. 忽略处理:SIG_IGN(ignore) 如果形如signal(2,SIG_IGN);,运行后不能通过2号信号停止,但可以通过3号信号进行停止。
  3. 自定义处理