[Linux] Save the signal of the process signal

1. Preservation of signals

1. Other common concepts related to signals

  • The actual execution of the signal processing action is called the signal delivery (Delivery)
  • The state between signal generation and delivery is called signal pending (Pending) .

A process can choose to block (Block) a signal. When a blocked signal is generated, it will remain in a pending state until the process unblocks the signal before performing the delivery action.

Note: blocking and ignoring are different, as long as the signal is blocked, it will not be delivered, while ignoring is an optional processing action after delivery .

2. The signal is represented in the kernel

Schematic representation of signals in the kernel:
insert image description here

There are three tables in the operating system kernel, two are bitmap structures ( blockand pending), and one is a function pointer array structure.

  • blockTable: Whether the bit at the corresponding position in the bitmap structure is 1, represents whether the signal is blocked.

  • pendingTable: Whether the bit at the corresponding position in the bitmap structure is 1, represents whether the signal is pending.

  • handlerTable: The table stores function pointers, and the function pointer in the corresponding subscript indicates which function to call when receiving the signal.
    (In the entire three tables, the data is logically transferred horizontally)

The signal inside SIG_DFLmeans to execute the default action, and SIG_IGNmeans to execute the ignore action. They are defined as follows:

insert image description here

The principle of the function we used before signalis to modify handlerthe function pointer at the corresponding position in the table.

explain:

  • Each signal has two flags indicating blocking (block) and pending (pending) , and a function pointer indicating processing actions. When a signal is generated, the kernel sets the pending flag of the signal in the process control block, and the flag is not cleared until the signal is delivered. In the example above, SIGHUPthe signal is neither blocked nor generated, and the default processing action is performed when it is delivered.

  • SIGINTThe signal has been generated, but is being blocked, so it cannot be delivered temporarily. Although its processing action is to ignore, this signal cannot be ignored before unblocking, because the process still has the opportunity to change the processing action before unblocking.

  • SIGQUITThe signal has not been generated, and once SIGQUITthe signal is generated, it will be blocked, and its processing action is a user-defined function sighandler.


What to do if a signal is generated multiple times before the process unblocks it?

POSIX.1 allows the system to deliver this signal one or more times. Linux is implemented in this way: regular signals are generated multiple times before delivery and only counted once , while real-time signals are generated multiple times before delivery and can be placed in a queue in turn.


3. Signal set in the system kernel

From the schematic representation of the signal in the kernel: each signal has only one bit pending flag, which is either 0 or 1, and the number of times the signal has been generated is not recorded, and the blocking flag is also represented in this way.

sigset_tTherefore, the pending and blocked flags can be stored with the same data type sigset_t, called a signal set. The essence of this type is a bitmap structure, which can represent the "valid" or "invalid" status of each signal. In the blocking signal The meaning of "active" and "inactive" in the set is whether the signal is blocked, while the meaning of "active" and "inactive" in the pending signal set is whether the signal is pending. The blocking signal set is also called the signal mask of the current process (Signal Mask) , and the "shielding" here should be understood as blocking rather than ignoring.

Introduction to sigset_t type

sigset_tIt is Linuxa data type provided to us by the operating system. Its underlying package is an longarray of types. We use each bit in this array to represent relevant information.

insert image description here

2. Signal set operation function

1. The operation function of sigset_t type

sigset_tThe type uses a bit for each signal to indicate the "valid" or "invalid" status. As for how to store these bits inside this type, it depends on the system implementation. From the user's point of view, it does not need to be concerned. The user can only call the following functions to Manipulate sigset_ tvariables instead of directly manipulating its internal data.

insert image description here

  • The function sigemptysetinitializes the signal set pointed to by set, and clears the corresponding bits of all signals in it, indicating that the signal set does not contain any valid signals.

  • The function sigfillsetinitializes the signal set pointed to by set, and sets the corresponding bits of all signals in it 1, indicating that the effective signals of this signal set include all signals supported by the system.

  • The function is to set the bit position corresponding to sigaddsetthe signal in the signal set pointed to by set assignum1

  • The function is to set the bit position corresponding to sigdelsetthe signal in the signal set pointed to by set assignum0

  • sigismemberIt is a Boolean function used to judge whether a signal set contains the signum signal, if it is included, it will return 1, if it is not included, it will return 0, and if it fails, it will return -1.

  • Note, before using sigset_ tthe variable of the type, it must be called sigemptysetor sigfillsetinitialized to make the signal set in a definite state. After the variable is initialized , it is possible to add or remove some valid signal sigset_tin the call sigaddsetand in the signal set.sigdelset

  • These four functions all return successfully 0and return on error-1

2. System calls about block tables

sigprocmaskThe calling function can read or change the signal mask (blocking signal set) of the process.

insert image description here

  • Parameters :

  • The first parameter is a flag bit, which has the following three options to choose from:
    (assuming the current signal mask is mask)

parameter Function
SIG_BLOCK set contains the signal we want to add to the current signal mask, which is equivalent to mask = mask | set
SIG_UNBLOCK set contains the signals we want to unblock from the current signal mask, equivalent to mask=mask&~set
SIG_SETMASK Set the current signal mask word to the value pointed to by set, which is equivalent to mask=set
  • The second parameter is a signal set.

  • The third signal is an output parameter. When the system blocksets a new signal set for the signal set, the content of the old signal set will be extracted and copied into oldsetit.

  • Return value : Return if the call is successful 0, return if the call fails -1, and the error code is set.

For blocked signals, if the blocking is unblocked, it will be delivered immediately, so if the call sigprocmaskunblocks several current pending signals, sigprocmaskat least one of the signals will be delivered before returning.

Below we use the code demonstration to prove our above theory. The following code means that we first mask 2the number , then print the old signal mask word, and then we print the signal mask word of the current process, and then print the signal mask of the current process When typing, we press the keyboard Ctrl + Cto see if the process will exit. If it does not exit, it means that the current signal is indeed blocked. After the last 5 seconds, the original signal will be restored. Signal No. 2 should be delivered immediately and the process will exit.

#include <iostream>
#include <signal.h>
#include <unistd.h>


// 打印sigset_t结构
void Printset(sigset_t* set)
{
    
    
    for (int i = 1; i <= 31;i++)
    {
    
    
        if (sigismember(set, i))
        {
    
    
            std::cout << "1";
        }
        else
        {
    
    
            std::cout << "0";
        }
    }
}

int main()
{
    
    
    // 变量的初始化
    int count = 0;
    sigset_t set, oset;
    sigemptyset(&set);
    sigemptyset(&oset);

    // 设置当前进程的信号屏蔽字
    sigaddset(&set, SIGINT);
    sigprocmask(SIG_SETMASK, &set, &oset);

    // 打印出老的信号屏蔽字
    std::cout << "老的信号屏蔽字是:";
    Printset(&oset);
    std::cout << std::endl;

    while (true)
    {
    
    
        std::cout << "目前信号屏蔽字是:";
        Printset(&set);
        std::cout << std::endl;
       

        if (count++ == 5)
        {
    
    
            // 恢复原来的信号屏蔽字
            std::cout << "恢复原来的信号屏蔽字" << std::endl;
            sigprocmask(SIG_SETMASK, &oset, &set);
        }
        sleep(1);
    }
    return 0;
}

The running results are shown in the figure: the results meet our expectations!

insert image description here

3. System calls about the pending table

We cannot modify the pending table, we can only view it through system calls.

insert image description here

sigpendingThis function is very simple. This function will read the pending signal set of the current process and send it out through the set parameter. If the call is successful, it will return 0, and if it fails, it will return -1.

Let's write an example to continue to verify our above principle: we first shield 2the number signal, and then print the pending signal set of the current process. At the beginning, because the process did not receive the signal, we should see all zeros, and then Then we send a 2number signal to the process. Since the number 2 signal is blocked, the pending signal set of the current process should be that the second bit is 1, and the others are all 0.

#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>

// 打印sigset_t结构
void Printset(sigset_t* set)
{
    
    
    for (int i = 1; i <= 31;i++)
    {
    
    
        if (sigismember(set, i))
        {
    
    
            std::cout << "1";
        }
        else
        {
    
    
            std::cout << "0";
        }
    }
}

int main()
{
    
    
    // 变量的初始化
    int count = 0;
    sigset_t set, oset, pending;
    sigemptyset(&set);
    sigemptyset(&oset);

    // 设置当前进程的信号屏蔽字
    sigaddset(&set, SIGINT);
    sigprocmask(SIG_SETMASK, &set, &oset);
    
    while (true)
    {
    
    
        // 打印未决信号集
        sigpending(&pending);
        std::cout << "目前的未决信号集是:";
        Printset(&pending);
        std::cout << std::endl;

        if (count++ == 5)
        {
    
    
            // 恢复原来的信号屏蔽字
            std::cout << "恢复原来的信号屏蔽字" << std::endl;
            sigprocmask(SIG_SETMASK, &oset, &set);
        }
        sleep(1);
    }
    return 0;
}

insert image description here

3. Conclusion

This chapter is about the preservation of process signals. The concept of signal preservation is too much, and the actual operation is relatively small. Therefore, it is necessary to understand block pending handlerthe table .

In the next chapter, we will continue to understand the processing of process signals in depth, and continue to improve our understanding of signals. Of course, if there are mistakes or deficiencies in this article, please comment or private message to discuss! See you next time, byebye!

Guess you like

Origin blog.csdn.net/qq_65207641/article/details/130850928