在Linux操作系统中,SIGUSR1和SIGUSR2是用户定义的信号,它们可以由进程发送给另一个进程,或者由进程发送给自己,用于实现用户间的通信或执行特定的处理。这些信号可以被捕获、忽略或采取默认动作。

一、信号

Linux系统共定义了64种信号,分为两大类:可靠信号不可靠信号,前32种信号为不可靠信号,后32种为可靠信号。

1.1 可靠信号与不可靠信号

  • 不可靠信号: 也称为非实时信号不支持排队,信号可能会丢失, 比如发送多次相同的信号, 进程只能收到一次. 信号值取值区间为1~31;
  • 可靠信号: 也称为实时信号,实时信号的关键特点是它们会排队,如果一个信号在被处理之前多次发送,它们会按照发送顺序排队,直到被处理。信号不会丢失,发多少次就可以收到多少次,信号值取值区间为32~64

1.1 相关shell命令

在 Linux 终端,可通过

  • kill -l 查看所有的信号,以及信号的默认动作。
  • man 7 signal 可查看 man 手册中关于信号详细介绍。

1.2 实时信号(可靠信号)

  • 会排队:发送实时信号时,如果目标进程暂时不处理信号(例如,它正处于不可中断的睡眠状态),信号可以在队列中等待,直到进程准备好处理它们。
  • 信号可以携带信息:发送实时信号时,可以携带额外的数据(通过sigqueue系统调用),接收方可以通过sigaction结构中的sa_sigaction字段获取这些数据。

下面介绍两个 常用 用户定义 实时信号

  • SIGUSR1(User-defined signal 1)

    1. 这是一个实时信号,意味着如果向进程发送多个SIGUSR1信号,它们不会被合并,而是按照发送的顺序排队
    2. SIGUSR1的默认动作是“终止进程”,但是进程可以捕获这个信号并为其分配一个信号处理函数,以执行特定的任务。
    3. 父进程 通常使用SIGUSR1信号来通知 子进程 进行某些操作,例如重新打开日志文件、重新加载配置文件等。
  • SIGUSR2(User-defined signal 1)

    1. SIGUSR2也是一个实时信号,可以用于用户自定义目的。
    2. 默认情况下,SIGUSR2的默认动作也是终止进程,但进程可以选择捕获这个信号并定义自己的处理函数。
    3. 开发者可以根据需要使用SIGUSR2信号,例如,可以让进程在收到信号时切换到不同的运行模式或执行特定的维护任务。

二、信号集

信号集(signal set)是指一个能够包含多个信号数据类型,它用于表示一组信号。信号集通常用于信号掩码(signal mask)和信号处理的相关操作中。进程可以使用 “信号集” 来指定它希望阻塞的信号(sigprocmask)、它希望等待的信号(sigwaitsigtimedwait),或者它想要处理的信号(sigpending)。

在Linux 用户态,信号集通过sigset_t类型来表示,这是一个能够包含所有信号的数据类型。信号集相关的操作可以通过一组函数来进行,这些函数包括:

函数 作用
int sigemptyset(sigset_t *set); 初始化一个信号集,使其不包含任何信号。
int sigfillset(sigset_t *set); 初始化一个信号集,使其包含所有信号。
int sigaddset(sigset_t *set, int signum); 信号集添加一个特定的信号。
int sigdelset(sigset_t *set, int signum); 信号集删除一个特定的信号。
int sigismember(const sigset_t *set, int signum); 检查一个信号是否是信号集的成员。

三、信号的3个处理过程

  1. 发送: 有对应的发送函数

  2. 接收: 有对应的接收函数

  3. 处理: 有对应的处理函数

下面逐一讲解:

3.1 发送

3.1.1 向自身发送信号(raise)

#include <stdio.h>
#include <signal.h>
int main() {
        raise(SIGKILL);
        //raise(SIGSTOP);
        printf("process run ok\n");
        return 0;
}

在这里插入图片描述

编译运行这一段程序,可见,程序还没打印字符串就结束了。可见这个函数可以用于程序自杀。

3.1.2 向别的’进程‘发送信号(kill)

写个死循环的代码让他运行着,然后编译下面的函数,kill掉目标死循环进程。

#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函数(该函数会自动扫面目标字符串,自动跳过开头的空白字符“换行、回车、空格”,遇到数字或正负号才开始转换,遇到非数字或字符串结束符’\0’时结束转换返回结果)

3.1.3 发送闹钟信号(alarm)

使用 alarm 来定时 seconds 发送一个 SIGALRM 信号,该信号的默认动作是终止进程:

  • 函数格式:unsigned int alarm(unsigned int seconds);

这个闹钟函数还挺有意思,下次专门写个博客描述一下。

3.2 接收(注册)

有老的signal函数,以及比较新的sigaction函数,见博客 sigaction函数与signal函数

3.3 处理

处理信号一般分三类:

  • 忽略
  • 默认处理
  • 自定义处理

如果设置为自定义处理,要保证 信号处理函数可重入函数

何为可重入函数???(详情可学习:浅谈可重入函数与不可重入函数
可重入函数就是可以被中断的函数,不可重入的函数会由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断,返回可能会出现问题。在信号处理函数中还要做到:

  • 不用全局变量和静态数据结构
  • 不要调用 mallocfree
  • 不要调用标准 IO 函数
  • 不能调用其它任何 “不可重入的函数”。

参考:read://https_dlonng.com/?url=https%3A%2F%2Fdlonng.com%2Fposts%2Fsignal

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐