[Signal] Signal saving {Data structure for managing signals: pending, block, handler; signal set; sigset_t type; signal set operation function; related system calls: sigpending, sigprocmask}

1. Related concepts

  • Signal delivery: The actual processing action of executing the signal is called signal delivery (Delivery), including default, ignore and custom capture.
  • Signal pending: The state between signal generation and delivery is called signal pending (Pending). Therefore, the signal bitmap mentioned above is also called Pending bitmap.
  • Signal blocking (shielding): A process can choose to block (Block) a signal. The blocked signal will remain in the pending state when it is generated, and the delivery action will not be executed until the process unblocks the signal.

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. Data structure for managing signals

In the process PCB, the signal’s pending bitmap, block bitmap, and handler function pointer array are data structures used to manage signal processing.

  1. Pending bitmap: Each process has a pending bitmap, used to record signals that have been received by the process but have not yet been processed (pending signals). When a signal is received, the corresponding bit is set to 1, indicating that the signal is pending. The flag is not cleared until the signal is delivered. A process can determine whether there are unhandled signals by examining the pending bitmap. In Linux, you can use the sigpending function to obtain the pending bitmap of the current process.

  2. Block bitmap: Each process has a block bitmap, used to specify the currently blocked signal. When a signal is blocked, the corresponding bit will be set to 1, indicating that the signal is blocked and will not be delivered temporarily. The process can control which signals are blocked by setting the block bitmap to avoid the process being interrupted at critical moments. In Linux, you can use the sigprocmask function to set and get the block bitmap of the current process.

  3. Handler function pointer array: Each process has an array of handler function pointers, used to manage signal processing functions. The index of the array corresponds to the number of the signal, and the elements of the array are function pointers pointing to the processing functions of the corresponding signals. When the process receives a signal, it will search the corresponding processing function in the handler function pointer array according to the signal number, and call the function to process the signal. In Linux, you can use the signal and sigaction functions to set and get signal processing functions.

hint:

  • The principle of registering a signal handler by the signal function: fill in the pointer of the signal processing function into the position corresponding to the signal number in the handler array.
  • The pending bitmap, block bitmap and handler function pointer array are for each process, and each process has its own independent bitmap and array. This enables independent management and processing of signals by different processes.

Signal processing process:

In the example above:

  • The SIGHUP(1) signal is neither blocked nor generated, and the default processing action is performed when it is delivered.
  • The SIGINT(2) 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 and then unblock.
  • The SIGQUIT(3) signal has been generated and has not been blocked, so it is delivered directly for processing. Its processing actions are user-defined procedures. Index the function pointer in the handler array by the signal number and execute the custom handler.

3. Signal set

3.1 sigset_t type

Pending bitmap and Block bitmap are also called signal sets. In Linux systems, the sigset_t type is a data type used to represent signal sets. It is a bit vector, each bit represents a specific signal. This type can represent the “valid” or “invalid” status of each signal.

The meaning of “valid” and “invalid” in the blocking signal set is whether the signal is blocked, while the meaning of “valid” and “invalid” in the pending signal set (Pending signal set) is whether the signal is in a pending state.

Related concepts:

  • The blocking signal set is also called the Signal Mask of the current process.
  • The pending signal set is also called Pending signal set.
  • The Handler function pointer array is also called Handler processing method table.

3.2 Signal set operation function

The system does not allow users to directly perform bit operations on signal sets, but provides a set of corresponding signal set operation functions.

In Linux systems, the following functions can be used to manipulate signal sets:

  1. int sigemptyset(sigset_t *set): Initialize the signal set set to an empty set, so that the bits corresponding to all signals are cleared to 0, indicating that the signal set does not contain any valid signal of. Returns 0 on success and -1 on failure.

  2. int sigfillset(sigset_t *set): Initialize the signal set set to a set containing all signals, so that the bit positions corresponding to all signals are 1. Returns 0 on success and -1 on failure.

  3. int sigaddset(sigset_t *set, int signum): Add the signal signum to the signal set set. Returns 0 on success and -1 on failure.

  4. int sigdelset(sigset_t *set, int signum): Delete the signal signum from the signal set set. Returns 0 on success and -1 on failure.

  5. int sigismember(const sigset_t *set, int signum): Judge whether the signal signum is in the signal set set. If it is, it returns 1, if not, it returns 0. If it does not, it returns – 1 and set errno.

These functions all need to pass in a pointer of type sigset_t as a parameter to specify the signal set to be operated. Among them, the signum parameter represents the number of the signal, and predefined macros such as SIGINT, SIGTERM, etc. can be used to represent specific signals.

Note: Before using a variable of type sigset_t, be sure to call sigemptyset or sigfillset for initialization so that the signal set is in a certain state. After initializing the sigset_t variable, you can add or delete a valid signal in the signal set by calling sigaddset and sigdelset.

3.3 Related system call interface

3.3.1 sigpending

The sigpending function is used to obtain the currently blocked pending signal set. Pending signals are signals that have been sent to the process but have not yet been processed.

The function prototype is as follows:

int sigpending(sigset_t *set);

parameter:

  1. The parameter set is a pointer to the sigset_t type, used to store the obtained pending signal set. (output type)

return value:

  • Success: return 0
  • Failure: Returns -1 and sets errno to indicate the cause of the error.

Usually, the sigpending function is used to query the currently blocked pending signal set in the signal processing function so that corresponding processing can be performed as needed.

3.3.2 sigprocmask

The sigprocmask function is used to obtain or change the signal mask word of the process, that is, to set or modify the set of signals currently blocked by the process.

The function prototype is as follows:

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

parameter:

  1. The parameter how specifies the modification method of the signal mask word, which can be one of the following three values:

    • SIG_BLOCK: Add the signal in the signal set pointed to by set to the current signal mask word. (mask = mask | set)
    • SIG_UNBLOCK: Remove the signal in the signal set pointed to by set from the current signal mask. (mask = mask & amp; ~set)
    • SIG_SETMASK: Set the current signal mask word to the signal set pointed to by set. (mask = set)
  2. The parameter set is a pointer to type sigset_t. If set is a non-null pointer, it is used to specify the signal set to be set. (input type)

  3. The parameter oldset is a pointer to type sigset_t. If oldset is a non-null pointer, it is used to store the signal mask word before modification. If you do not need to save the old signal mask word, you can set it to NULL. (output type)

return value:

  • Success: return 0
  • Failure: Returns -1 and sets errno to indicate the cause of the error.

By calling the sigprocmask function, you can control the blocking and unblocking of specific signals by the process. This is useful for temporarily blocking certain signals under certain circumstances, or for restoring a previous signal blocking state.

4. Test program

4.1 Try to catch all common signals

Test code:

mysignal.cc: Try to capture numbers [1,31], all ordinary signals

void CatchSig(int signum)
{<!-- -->
    cout << "Caught a signal:" << signum << endl;
}

void test1()
{<!-- -->
    for (int i = 1; i <= 31; + + i)
    {<!-- -->
        signal(i, CatchSig);
    }
    while(1);
}

sendsig.sh: Write a shell script to send signals [1,31]

#!/bin/bash

i=1
id=$(pidof mysignal) //Get the PID of the mysignal process
while [ $i -le 31 ]
do
    if [ $i -eq 9 ];then //Skip the SIGKILL(9) signal
        let i++
        continue
    fi
    if [ $i -eq 19 ];then //Skip the SIGSTOP(19) signal
        let i++
        continue
    fi
    kill -$i $id //kill command sends signal
    echo "kill -$i $id"
    let i++
    sleep 1
done

operation result:

The SIGKILL(9) signal and the SIGSTOP(19) signal cannot be captured; when signal No. 19 is sent to suspend the process, and then the SIGCONT (18) signal is sent, the process will continue to run.

4.2 First block signal No. 2, then unblock it, and print and observe the pending signal set

Test code: first block signal No. 2, then send signal No. 2 to the process, and finally unblock it. During the process, the pending signal set is printed and observed.

void ShowPending(const sigset_t *ppset)
{<!-- -->
    for (int i = 1; i <= 31; + + i)
    {<!-- -->
        if (sigismember(ppset, i))
            cout << "1";
        else
            cout << "0";
    }

    cout << endl;
}

void test2()
{<!-- -->
    // You need to capture signal No. 2, otherwise the process will exit immediately after unblocking.
    signal(2, CatchSig);
    //Define signal set object
    sigset_t bset, obset, pset;
    //Initialize the signal set object
    sigemptyset( & amp;bset);
    sigemptyset( & amp;bset);
    sigemptyset( & amp;bset);
    //Add the signal to be shielded to bset
    sigaddset( & amp;bset, SIGINT);
    //Block the bset signal set [By default, the process will not block any signals]
    cout << "Blocking signal No. 2!" << endl;
    sigprocmask(SIG_BLOCK, & amp;bset, & amp;obset);
    int cnt = 0;

    //Print and observe the pending signal set
    while(true)
    {<!-- -->
        // Get the pending signal set of the current process
        sigpending( & amp;pset);
        // Display the undelivered signals in the pending signal set
        ShowPending( & amp;pset);
        sleep(1);
         + + cnt;
        // Unblock after 10 seconds
        if (cnt == 10)
        {<!-- -->
            // By default, when the block for signal No. 2 is released, it will indeed be delivered.
            // But the default handling action for signal No. 2 is to terminate the process!
            //Need to capture signal No. 2
            cout << "Unblock signal No. 2!" << endl;
            sigprocmask(SIG_SETMASK, & amp;obset, nullptr);
        }
    }
}

operation result:

4.3 Try to block all signals

Test code: try to block [1,31], all ordinary signals

void test3()
{<!-- -->
    sigset_t bset, pset;
    // Initialize the signal set object and set all positions to 1, blocking all signals
    sigfillset( & amp;bset);
    cout << "Block all signals!" << endl;
    sigprocmask(SIG_BLOCK, & amp;bset, nullptr);
    while(true)
    {<!-- -->
        sigpending( & amp;pset);
        ShowPending( & amp;pset);
        sleep(1);
    }
}

operation result:

The SIGKILL(9) signal and the SIGSTOP(19) signal cannot be blocked; when signal No. 19 is sent to suspend the process, and then the SIGCONT (18) signal is sent, the process will continue to run.

4.4 Summary

  1. The SIGKILL(9) signal and the SIGSTOP(19) signal cannot be caught or blocked.
  2. After sending signal No. 19 to suspend the process, and then sending the SIGCONT(18) signal, it can be successfully delivered and the process can continue to run.
  3. The essence of sending a signal is to set the bit position of the signal corresponding to the pending signal set of the target process to 1. If the signal is not blocked, it is delivered directly, and then the pending flag of the signal is cleared.
  4. When a signal is received and blocked, the corresponding bit of the pending signal set will be set to 1, indicating that the signal is in a pending state. The signal cannot be delivered until it is unblocked. Finally, the pending flag of the signal is cleared < /strong>.
  5. How to operate the pending signal set:
    • All signal generation methods can modify the pending signal set;
    • The system call sigpending is used to obtain the pending signal set of the current process.
  6. How to operate signal masking words:
    • The system call sigprocmask is used to obtain or modify the signal mask word of the current process.
  7. How to operate the Handler processing method table:
    • The system call signal is used to modify the Handler processing method table of the current process.
    • The system call sigaction is used to obtain or modify the Handler processing method table of the current process (described in the next chapter)
syntaxbug.com © 2021 All Rights Reserved.