linux信号总结
信号
A给B发送信号,B收到信号之前执行自己的代码,收到信号后,不管执行到程序的什么位置,都要暂停执行,去处理信号,处理完毕后在继续执行。
与硬件中断类似(异步模式)。但信号是软件层面上实现的中断,早期被称为软中断.
信号的共性:简单、不能携带大量信息、满足条件才能发送。
信号的特质:由于信号是通过软件方法实现,其实现手段导致信号有很强的延时性。但对用户来说,这个延迟时间非常短,不易察觉。
每个进程收到的所有信号,都是由内核负责发送以及处理的
与信号相关的事件和状态
产生信号的方式如下表:
事件 | 例子 |
---|---|
按键产生 | Ctrl+c 、Ctrl+z 、Ctrl+\ |
系统调用产生 | kill 、raise 、 abort |
软件条件产生 | 定时器alarm |
硬件异常产生 | 非法访问内存(段错误)、除0(浮点数例外)、内存对齐出错(总线错误) |
命令产生 | kill |
递达:递送并且到达进程。
未决:产生和递达之间的状态。主要由于阻塞(屏蔽)导致该状态
信号处理的方式:
- 执行默认动作(即进程不去处理)
- 忽略(丢弃)
- 捕捉(调用用户处理函数)
在linux的实现PCB的结构体task_struct
中包含了信号相关的信息,主要是指阻塞信号集和未决信号集。
- 阻塞信号集(信号屏蔽字):将某些信号加入集合,对他们设置屏蔽(对应位 置1),当屏蔽某信号后,在收到该信号,该信号的处理将延后(解除屏蔽后)
- 未决信号集:
- 信号产生,未决信号集中描述该信号的位立即翻转为1, 表示信号处于为未决状态。当信号被处理对应位翻转回0。这一时刻往往非常短暂。
- 信号产生后由于某些原因(主要是阻塞)不能抵达。这类信号的集合称为未决信号集。在屏蔽解除前,信号一直处于未决状态。
不存在编号为0的信号。其中1-31号信号称之为常规信号(也叫普通信号或标准信号),34-64称为实时信号,驱动编程与硬件相关。名字上区别不大。而前32个名字各不相同。
信号值被定义在文件 /usr/include/bits/signum.h
中,其源文件是 /usr/src/linux/kernel/signal.c
。
在 Linux 下,可以查看 signal(7) 手册页来查阅信号名列表、信号值、默认的行为和它们是否可以被捕获。其命令如下所示:
1 | man 7 signal |
下表列出Linux中常见的进程信号:
编号 | 名称 | 事件 | 默认操作 |
---|---|---|---|
1 | SIGHUP | (Hangup)当你不在控制终端时,或者当你关闭gnome-termnal或断开modem内核会产生该信号。由于后台进程没有控制的终端,因而它们常用SIGUP 来发出需要重新读取其配置文件的信号。 |
(Abort)挂断控制终端信号或进程。 |
2 | SIGINT | (Interrupt)来自键盘的中断。通常终端驱动程序会将其与Ctrl+c 绑定 |
(Abort)终止程序 |
3 | SIGQUIT | (Interrupt)来自键盘的中断。通常终端驱动程序会将其与Ctrl+\ 绑定 |
(Dump)程序被终止并产生dump core文件。 |
4 | SIGILL | (Illegal Instruction)程序出错或执行了一个非法的操作命令 | (Dump)程序被终止并产生dump core文件。 |
5 | SIGTRAP | (Breakpoint/Trace Trap)调试用,跟踪断点 | |
6 | SIGABRT | (Abort)放弃执行,异常结束。 | (Dump)程序被终止并产生dump core文件。 |
7 | SIGBUS | (Bus)当进程引起一个总线错误时,BUS 信号将被发送到进程。例如,访问了一部分未定义的内存对象 | (Dump)程序被终止并产生dump core文件。 |
8 | SIGFPE | (Floating Point Exeception)浮点异常 | (Dump)程序被终止并产生dump core文件。 |
9 | SIGKILL | (Kill)程序被终止。该信号不能被捕获或被忽略。想立即终止一个进程就发送信号9.注意程序将没有任何机会做清理工作。 | (Abort) 程序被终止。 |
10 | SIGUSR1 | (User defined Signal 1) 用户定义的信号。 | (Abort) 进程被终止。 |
11 | SIGSEGV | (Segmentation Violation) 当程序引用无效的内存时会产生此信号。比如:寻址没有映射的内存;寻址未许可的内存。 | (Dump) 程序被终止并产生 dump core 文件。 |
12 | SIGUSR2 | (User defined Signal 2) 保留给用户程序用于 IPC 或其他目的。 | (Abort) 进程被终止。 |
13 | SIGPIPE | (Pipe) 当程序向一个套接字或管道写时由于没有读者而产生该信号 | (Abort) 进程被终止。 |
14 | SIGALRM | (Alarm) 该信号会在用户调用 alarm系统调用所设置的延迟秒数到后产生。该信号常用判别于系统调用超时。 | (Abort) 进程被终止。 |
15 | SIGTERM | (Terminate) 用于和善地要求一个程序终止。它是 kill的默认信号。与 SIGKILL 不同,该信号能被捕获,这样就能在退出运行前做清理工作。 | (Abort) 进程被终止。 |
16 | SIGSTKFLT | (Stack fault on coprocessor) 协处理器堆栈错误。 | (Abort) 进程被终止。 |
17 | SIGCHLD | (Child) 停止或终止子进程。可改变其含义挪作它用。 | (Ignore) 子进程停止或结束。 |
18 | SIGCONT | (Continue) 该信号致使被 SIGSTOP 停止的进程恢复运行。可以被捕获。 | (Continue) 恢复进程 的执行。 |
19 | SIGSTOP | (Stop) 停止进程的运行。该信号不可被捕获或忽略 | (Stop) 停止进程运行。 |
20 | SIGTSTP | (Terminal Stop) 向终端发送停止键序列。该信号可以被捕获或忽略。 | (Stop) 停止进程运行。 |
21 | SIGTTIN | (Terminal Input on Background) 后台进程试图从一个不再被控制的终端上读取数据,此时该进程将被停止,直到收到 SIGCONT 信号。该信号可以被捕获或忽略。 | (Stop) 停止进程运行。 |
22 | SIGTTOU | (TTY Output on Background) 后台进程试图向一个不再被控制的终端上输出数据,此时该进程将被停止,直到收到 SIGCONT 信号。该信号可被捕获或忽略。 | (Stop) 停止进程运行。 |
tips: 只有每个信号所对应的事件发生了,该信号才会被递送(但不一定递达),不应乱发信号!
当程序运行的过程中出现异常终止或崩溃,系统就会将程序崩溃时的内存、寄存器状态、堆栈指针、内存管理信息记录下来,保存在一个文件中,叫作核心转储(Core Dump)
可以通过以下操作开启核心转储并修改核心转储文件的保存路径
1 | ulimit -c # 默认关闭 0 |
若有一个核心转储文件,可以使用gdb进行调试
1 | gdb [filename] [core file] |
系统调用产生信号
kill函数
该函数用来发生一个信号给一个进程
函数原型
1 |
|
参数说明
1 | @param: |
进程组:每个进程都属于一个进程组,进程组是一个或多个进程集合,他们互相关联,共同完成一个实体任务,每个进程组都有一个进程组长,默认进程组ID与进程组长ID相同.
权限保护:
- 超级用户可以发送信号给任意用户
- 普通用户没有权限向系统用户以及其他普通用户发送信号
- 普通用户的基本规则是:发送者实际或有效UID==接受者实际或有效UID
封装了kill函数
的几个发出信号的函数
raise函数
当进程中只有一个线程的适合它等价于
kill(getuid(), sig)
当进程中只有多个线程的适合它等价于pthread_kill(pthread_self(), sig)
1 |
|
abort函数
该函数永远没有返回值,其等价于
raise(SIGABRT)
1 |
|
软件条件产生信号
alarm函数
每个进程都有且只有唯一一个定时器, 本质就是PCB中的alarm
变量。
在指定seconds后(设置current->alarm
),在CPU数了seconds包含的总系统滴答后,即当前进程task_struct
的成员变量alarm
小于 系统从开机算起的滴答数jiffies
(current->alarm && current->alarm < jiffies
)时,内核会给当前进程发送SIGALRM信号
。系统收到该信号,默认动作终止。
函数原型
1 |
|
参数说明
1 | @param: |
用法示例
1 | alarm(5); // 设置定时时间为5 |
setitimer函数
设置定时器。可代替alarm函数。精度微秒us,可实现周期定时
函数原型
1 |
|
参数说明
1 | @param: |
e.g.
1 |
|
信号集操作函数
自定义信号集
sigset_t
类型本质是位图,但不应该直接使用位操作,而应该使用下面的函数来保证程序的可移植性。
设定自定义信号集函数如下表,它们的头文件都是signal.h
函数原型 | 功能 | 返回值 |
---|---|---|
int sigemptyset(sigset_t *set); |
将信号集set 全清0 |
return 0 on sucess and -1 on error |
int sigfillset(sigset_t *set); |
将信号集set 全置1 |
return 0 on sucess and -1 on error |
int sigaddset(sigset_t *set, int signum); |
将信号signum 加入集合set 中 |
return 0 on sucess and -1 on error |
int sigdelset(sigset_t *set, int signum); |
将集合set 中删除信号signum |
return 0 on sucess and -1 on error |
int sigismember(const sigset_t *set, int signum); |
查找信号signum 是否在集合set 中 |
signum 在集合set 中:1,不在:0,出错:-1 |
操作信号屏蔽字
可以用sigprocmask函数来操作PCB中的信号屏蔽字来达到屏蔽信号、解除屏蔽信号的效果。
函数原型
1 |
|
参数说明
1 | @param: |
查看未决信号集
可以用
sigpending
函数查看PCB中未决信号的状态
函数原型
1 |
|
参数说明
1 | @param: |
使用示例
1 |
|
信号捕捉
信号处理程序的调用方式如下图
signal函数
signal
函数用于注册一个信号的捕捉函数,它由ANSI定义,由于历史原因在不同版本的Unix和不同版本的Linux中可能有不同的行为。因此应该尽量避免使用它,取而代之使用sigaction
函数。
函数原型
1 |
|
sigaction函数
修改信号处理动作,用来注册一个信号的捕捉函数
函数原型
1 |
|
参数说明
1 | @param: |
e.g. 简单的使用测试
1 |
|
信号捕捉特性
- 程序正常运行时,PCB中有一个信号屏蔽集,当注册了某个捕捉函数,且捕捉到了该信号后,此时会有一个临时的屏蔽集
sa_mask
,在函数回调过程中,信号的屏蔽集为临时的屏蔽集和原来的屏蔽集的并集,函数调用结束后,再恢复到原来的屏蔽集。 - xxx信号捕捉函数执行期间,xxx信号被自动屏蔽.
- 阻塞的常规信号不支持排队,产生多次只记录一次.
后32个实时信号支持排队
使用信号回收子进程
SIGCHLD
信号的产生条件:
- 子进程终止时
- 子进程接受到
SIGSTOP
信号停止时 - 子进程处于停止态,接受到
SIGCONT
后唤醒时
e.g. 借助SIGCHLD
信号回收子进程
1 |
|