[Linux] Signal sets and related functions (sigemptyset, sigfillset, sigprocmask)

orange color

1, Signal collection

  • A collection of multiple signals is called a signal set, and its system data type is sigset_t.

  • There are two very important signal sets in PCB, one is called "blocked signal set" and the other is "pending signal set". Signals are generated but not processed (pending). All unprocessed signals are stored in a set (pending signal set) in the kernel. This pending signal needs to be processed. Before processing, it needs to be compared with another signal set (blocking signal set). During processing, the flag bit in the blocking signal set is queried to see whether blocking is set for the signal. .

  • The "pending" of a signal is a state, which refers to the period from the generation of the signal to the time before the signal is processed.

  • "Blocking" of a signal is a switching action, which means preventing the signal from being processed, but not preventing the signal from being generated.

  • Signal blocking allows the system to temporarily retain the signal for later transmission. Since there are other ways to make the system ignore signals, generally the blocking of signals is only temporary, just to prevent signals from interrupting sensitive operations.

2. Custom signal set related functions

以下信号集相关的函数都是对自定义的信号集进行操作

/*

    int sigemptyset(sigset_t *set);
        - 功能:清空信号集中的数据,将信号集中的所有的标志位置为0
        - 参数:set,传出参数,需要操作的信号集
        - 返回值:成功返回0, 失败返回-1

    int sigfillset(sigset_t *set);
        - 功能:将信号集中的所有的标志位置为1
        - 参数:set,传出参数,需要操作的信号集
        - 返回值:成功返回0, 失败返回-1

    int sigaddset(sigset_t *set, int signum);
        - 功能:设置信号集中的某一个信号对应的标志位为1,表示阻塞这个信号
        - 参数:
            - set:传出参数,需要操作的信号集
            - signum:需要设置阻塞的那个信号
        - 返回值:成功返回0, 失败返回-1

    int sigdelset(sigset_t *set, int signum);
        - 功能:设置信号集中的某一个信号对应的标志位为0,表示不阻塞这个信号
        - 参数:
            - set:传出参数,需要操作的信号集
            - signum:需要设置不阻塞的那个信号
        - 返回值:成功返回0, 失败返回-1

    int sigismember(const sigset_t *set, int signum);
        - 功能:判断某个信号是否阻塞
        - 参数:
            - set:需要操作的信号集
            - signum:需要判断的那个信号
        - 返回值:
            1 : signum被阻塞
            0 : signum不阻塞
            -1 : 失败

*/

Code example

#include <signal.h>
#include <stdio.h>

int main() {
    
    

    // 创建一个信号集,一开始可能有某些标志位为1,所以下一步需要清空
    sigset_t set;

    // 清空信号集的内容
    sigemptyset(&set);

    // 判断 SIGINT 是否在信号集 set 里
    int ret = sigismember(&set, SIGINT);
    if(ret == 0) {
    
    
        printf("SIGINT 不阻塞\n");
    } else if(ret == 1) {
    
    
        printf("SIGINT 阻塞\n");
    }

    // 添加几个信号到信号集中
    sigaddset(&set, SIGINT);
    sigaddset(&set, SIGQUIT);

    // 判断SIGINT是否在信号集中
    ret = sigismember(&set, SIGINT);
    if(ret == 0) {
    
    
        printf("SIGINT 不阻塞\n");
    } else if(ret == 1) {
    
    
        printf("SIGINT 阻塞\n");
    }

    // 判断SIGQUIT是否在信号集中
    ret = sigismember(&set, SIGQUIT);
    if(ret == 0) {
    
    
        printf("SIGQUIT 不阻塞\n");
    } else if(ret == 1) {
    
    
        printf("SIGQUIT 阻塞\n");
    }

    // 从信号集中删除一个信号
    sigdelset(&set, SIGQUIT);

    // 判断SIGQUIT是否在信号集中
    ret = sigismember(&set, SIGQUIT);
    if(ret == 0) {
    
    
        printf("SIGQUIT 不阻塞\n");
    } else if(ret == 1) {
    
    
        printf("SIGQUIT 阻塞\n");
    }

    return 0;
}

Insert image description here

3. sigprocmask function

Function analysis

/*
    int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
        - 功能:将自定义信号集中的数据设置到内核中(设置阻塞,解除阻塞,替换)
        - 参数:
            - how : 如何对内核阻塞信号集进行处理
                SIG_BLOCK: 将用户设置的阻塞信号集添加到内核中,内核中原来的数据不变
                    假设内核中默认的阻塞信号集是mask, mask | set
                SIG_UNBLOCK: 根据用户设置的数据,对内核中的数据进行解除阻塞
                    mask &= ~set(也就是先对set取反,再与mask按位与),假设内核中默认的阻塞信号集是mask
                SIG_SETMASK:覆盖内核中原来的值
            
            - set :已经初始化好的用户自定义的信号集
            - oldset : 保存设置之前的内核中的阻塞信号集的状态,可以是 NULL
        - 返回值:
            成功:0
            失败:-1
                设置错误号:EFAULT、EINVAL

    int sigpending(sigset_t *set);
        - 功能:获取内核中的未决信号集
        - 参数:set,传出参数,保存的是内核中的未决信号集中的信息。
*/

Code example

The code first customizes the signal set and puts the SIGINT and SIGQUIT signals into it. Then the blocking signal set in the kernel was modified through the sigprocmask function. Then an infinite loop is set up, a signal set pendingset is created within the loop, the data of the current pending signal set is put into the pendingset, and then the first 31 bits of the signal set are traversed.

When you run this program, the first output will be 0. Then, before looping 10 times, press ctrl+c, and the second printed digit will change from 0 to 1 (actually, this is when you press ctrl+c , which means sending the SIGINT signal to the system, but the signal has been set to 1 in the blocking signal set in the kernel through the above operation, so when the system receives the signal, it will also be blocked, so the signal status is not resolved, the second bit of the printed pending signal set (representing SIGINT) becomes 1).

After 10 cycles, the blockage was released. Previously, pressing ctrl+c could not stop the infinite loop, but after pressing ctrl+c and looping 10 times, the infinite loop will automatically stop, because after setting the program 10 times, the signal blocking we set before will be released, and the system will no longer Block the previously sent SIGINT signal, so the SIGINT signal takes effect and stops the process.

// 编写一个程序,把所有的常规信号(1-31)的未决状态打印到屏幕
// 设置某些信号是阻塞的,通过键盘产生这些信号

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    
    

    // 设置2、3号信号阻塞
    sigset_t set;
    sigemptyset(&set);
    // 将2号和3号信号添加到信号集中
    sigaddset(&set, SIGINT);
    sigaddset(&set, SIGQUIT);

    // 修改内核中的阻塞信号集
    sigprocmask(SIG_BLOCK, &set, NULL);

    int num = 0;

    while(1) {
    
    
        num++;
        // 获取当前的未决信号集的数据
        sigset_t pendingset;
        sigemptyset(&pendingset);
        sigpending(&pendingset);

        // 遍历前32位
        for(int i = 1; i <= 31; i++) {
    
    
            if(sigismember(&pendingset, i) == 1) {
    
    
                printf("1");
            }else if(sigismember(&pendingset, i) == 0) {
    
    
                printf("0");
            }else {
    
    
                perror("sigismember");
                exit(0);
            }
        }

        printf("\n");
        sleep(1);
        if(num == 10) {
    
    
            // 解除阻塞
            sigprocmask(SIG_UNBLOCK, &set, NULL);
        }

    }

    return 0;
} 

Insert image description here

Guess you like

Origin blog.csdn.net/mhyasadj/article/details/130792160