Linux signal
- 一、信号分类
- 二、什么是信号集?
- 三、信号的3个处理过程
-
- [3.1 发送信号](#3.1 发送信号)
-
- [3.1.1 向自身发送信号(raise)](#3.1.1 向自身发送信号(raise))
- [3.1.2 向别的进程发送信号(kill)](#3.1.2 向别的进程发送信号(kill))
- [3.1.3 发送闹钟信号(alarm)](#3.1.3 发送闹钟信号(alarm))
- [3.2 接收(注册)信号](#3.2 接收(注册)信号)
- [3.3 处理信号](#3.3 处理信号)
在Linux操作系统中,SIGUSR1和SIGUSR2是用户定义的信号,它们可以由进程发送给另一个进程,或者由进程发送给自己,用于实现用户间的通信或执行特定的处理。这些信号可以被捕获、忽略或采取默认动作。
一、信号分类
Linux系统共定义了64种信号,分为两大类:可靠信号与不可靠信号,前32种信号为不可靠信号,后32种为可靠信号。
- 不可靠信号: 也称为非实时信号,不支持排队,信号可能会丢失, 比如发送多次相同的信号, 进程只能收到一次. 信号值取值区间为1~31;
- 可靠信号: 也称为实时信号,实时信号的关键特点是它们会排队,如果一个信号在被处理之前多次发送,它们会按照发送顺序排队,直到被处理。信号不会丢失, 发多少次, 就可以收到多少次. 信号值取值区间为32~64。
在终端,可通过
- kill -l 查看所有的signal信号,以及信号的默认动作。
- man 7 signal 可查看man手册中关于signal的详细介绍
相比于非实时信号,实时信号有如下特点:
- 信号可以排队。
- 信号可以等待:发送实时信号时,如果目标进程暂时不处理信号(例如,它正处于不可中断的睡眠状态),信号可以在队列中等待,直到进程准备好处理它们。
- 信号可以携带信息:发送实时信号时,可以携带额外的数据(通过sigqueue系统调用),接收方可以通过sigaction结构中的sa_sigaction字段获取这些数据。
SIGUSR1(User-defined signal 1)
- 这是一个实时信号,意味着如果向进程发送多个SIGUSR1信号,它们不会被合并,而是按照发送的顺序排队。
- SIGUSR1的默认动作是终止进程,但是进程可以捕获这个信号并为其分配一个信号处理函数,以执行特定的任务。
- 父进程通常使用SIGUSR1信号来通知子进程进行某些操作,例如重新打开日志文件、重新加载配置文件等。
SIGUSR2(User-defined signal 1)
-
与SIGUSR1类似,SIGUSR2也是一个实时信号,可以用于用户自定义目的。
-
默认情况下,SIGUSR2的默认动作也是终止进程,但进程可以选择捕获这个信号并定义自己的处理函数。
-
开发者可以根据需要使用SIGUSR2信号,例如,可以让进程在收到信号时切换到不同的运行模式或执行特定的维护任务。
二、什么是信号集?
信号集(signal set)是指一个能够包含多个信号的数据类型,它用于表示一组信号。信号集通常用于信号掩码(signal mask)和信号处理的相关操作中。进程可以使用信号集来指定它希望阻塞的信号(sigprocmask)、它希望等待的信号(sigwait或sigtimedwait),或者它想要处理的信号(sigpending)。
在Linux中,信号集通过sigset_t类型来表示,这是一个能够包含所有信号的数据类型。信号集相关的操作可以通过一组函数来进行,这些函数包括:
函数 | 作用 |
---|---|
sigemptyset | 初始化一个信号集,使其不包含任何信号。 |
sigfillset | 初始化一个信号集,使其包含所有信号。 |
sigaddset | 向信号集中添加一个特定的信号。 |
sigdelset | 从信号集中删除一个特定的信号。 |
sigismember | 检查一个信号是否是信号集的成员。 |
三、信号的3个处理过程
- 发送信号: 有对应的发送函数
- 接收信号: 有对应的接收函数
- 处理信号: 有对应的处理函数
下面逐一讲解:
3.1 发送信号
3.1.1 向自身发送信号(raise)
c
#include <stdio.h>
#include <signal.h>
int main() {
raise(SIGKILL);
//raise(SIGSTOP);
printf("process run ok\n");
return 0;
}
编译运行这一段程序,可见,程序还没打印字符串就结束了。可见这个函数可以用于程序自杀。
3.1.2 向别的进程发送信号(kill)
写个死循环的代码让他运行着,然后编译下面的函数,kill掉目标死循环进程。
c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
int main(int argc, char *argv[]) {
char *endptr = NULL;
unsigned long int val;
val = strtoul(argv[1], &endptr, 10);
printf("val = %d \n",val);
kill(val, SIGKILL);
printf("Has kill\n");
return 0;
}
这里之所以输入6564dfd是为了试验一下strtoul函数
3.1.3 发送闹钟信号(alarm)
使用 alarm 来定时 seconds 发送一个 SIGALRM 信号,该信号的默认动作是终止进程:
函数格式:
unsigned int alarm(unsigned int seconds);
这个闹钟函数还挺有意思,下次专门写个博客描述一下。
3.2 接收(注册)信号
3.3 处理信号
处理信号一般分三类:忽略,默认处理、自定义处理。
如果设置为自定义处理,要保证信号处理函数为可重入函数。何为可重入函数???
可重入函数就是可以被中断的函数,不可重入的函数会由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断,返回可能会出现问题。在信号处理函数中还要做到:
1、不要使用带有全局静态数据结构的函数
2、不要调用 malloc 和 free
3、不要调用标准 IO 函数
参考:read://https_dlonng.com/?url=https%3A%2F%2Fdlonng.com%2Fposts%2Fsignal