Linux - Signal Summary 1

To understand the signal, first look at a scenario:

1. The user enters a command to start a foreground process under the shell

2. The user presses the Ctrl+C key, a hardware interrupt will be generated at this time

3. If the CPU is currently executing the code of this process, the user and space code of the improved process is suspended, and the CPU switches from user mode to kernel mode to handle hardware interrupts

4. The terminal driver interprets the Ctrl+C key as a SIGINT signal, which is recorded in the PCB of the process (it can also be said that a SIGINT signal is sent to the process)

5. Before returning from the kernel to the user space code to continue execution at a certain time, first process the signal recorded in the PCB, and find that there is a SIGINT signal to be processed, and the default processing action of this signal is to terminate the process, so the process is terminated directly userspace code execution without returning it

Use the kill -l command to view the list of system signals:


Each signal has a number and a macro definition name . The generation conditions and default processing actions of the above signals are described in detail in signal(7):

man 7 signal

The way to generate the signal:

    1. The user presses some keys in the terminal (such as Ctrl+C, etc.)

    2. The hardware abnormality generates a signal

    3. A process calls the kill(2) function to send a signal to another process

    4. Software Condition Generation

Signal processing method:

    1. Ignore the signal

    2. Execute the default processing action of the signal

    3. Execute user-defined actions (signal capture )

The generation of the signal is described in detail below :

1. Generate signals through terminal keys

    The default processing action of SIGINT is to terminate the process, and the default processing action of SIGQUIT is to terminate the process Core Dump (when a process is to be terminated abnormally, you can choose to save all the user space memory data of the process to the disk, and the file name is usually core. It is called Core Dump. By default, it is not allowed to generate core files, because the core files may contain user passwords and other information. During the development and debugging phase, you can use the ulimit command to change this limit, allowing core files to be generated, and the maximum core file is allowed to be 1024K)


Write an infinite loop to verify:


Run the program above, and enter Ctrl+\ in the terminal (note that Ctrl+C does not display Core Dump)


2. Call the system function to send a signal to the process

First execute the infinite loop program in the background, and then send it a SIGSEGV signal with the kill command:

    ·The reason why you need to press Enter once more to display the Segmentation fault is because the 7715 process has already returned to the Shell prompt to wait for the user to enter the next command before the 7715 process is terminated. The Shell does not want the Segmentation fault information to be interleaved with the user's input, so Displayed after waiting for user input.

   · The kill command to specify a certain signal can be written in many ways. The above command can also be written as kill -11 7715 , where 11 is the number of the signal SIGSEGV.

The kill command is implemented by calling the kill function, which can send a specified signal to a specified process. The raise function can send a specified signal to the current process (send a signal to yourself)

#include <signal.h>
int kill(pid_t pid,int signo);
int raise(int signo);
Both functions return 0 on success and -1 on error

The abort function causes the current process to receive a signal and terminate abnormally:

#include <stdlib.h>
void abort(void)
Just like the exit function, the abort function will always succeed, so there is no return value

3. Signals are generated by software conditions

    SIGPIPE is a signal generated by software conditions . It has been introduced in the pipeline ( https://blog.csdn.net/tangduobutian/article/details/79638121 ). The following mainly introduces the alarm function and the SIGALRM signal:

#include <unistd.h>
unsigned int alarm(unsigned int seconds);
Calling the alarm function can set an alarm clock, that is, tell the kernel to send a SIGALRM signal to the current process after second seconds. The default processing action of the signal is to terminate the current process.
The return value of this function is 0 or the number of seconds remaining in the previously set alarm time

example:

    #include <stdio.h>
    #include <unistd.h>
    
    intmain()
    {
        int count=14;
        alarm(3);
        for(;1;count++){
            printf("count == %d\n",count);
        }
        return 0;
    }

The function of this program is to count continuously within 3 seconds, and it will be terminated by the SIGALRM signal after 3 seconds.

blocking signal

1. Some other related common concepts of 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)

    Processes can choose to block a signal

    The blocked signal generation will remain in the pending state until the process unblocks the signal before executing the delivery action

    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. Representation in the kernel


                                                    Schematic representation of signals in the kernel

      Each signal has two flag bits indicating blocking and pending, and a function pointer indicating processing action. When a signal is generated, the kernel sets the pending flag of the signal in the process control block, and the signal is not released until the signal is delivered. logo.
     In the example above:    
     · The SIGHUP signal is not blocked or generated, and the default processing action is performed when it is delivered
     · The SIGINT signal has been generated, but is being blocked, so it cannot be delivered temporarily. Although its processing action is ignored, this signal cannot be ignored before unblocking, because the process still has the opportunity to change the processing action and then unblock it
    · The  SIGQUIT signal has not been generated. Once the SIGQUIT signal is generated, it will be blocked. Its processing action is the user-defined function sighhandler
What if a signal is generated multiple times before the process unblocks it?
    · POSIX 1 allows the signal to be delivered one or more times
    Linux is implemented in this way: regular signals are only counted once before delivery (that is, ordinary signals are allowed to be lost), while real-time signals are generated multiple times before they are delivered and can be placed in a queue in turn 
3.sigset_t

    As can be seen from the above figure, each signal has only a pending flag of one bit, which is either 0 or 1. It does not record how many times the signal is generated, and the blocking flag is also represented in this way. Therefore, pending and blocked flags can be stored using the same data type sigset_t, called a signal set, which can represent the "valid" or "invalid" state of each signal.

4. Signal set operation function

#include <signal.h>
int sigemptyset(sigset_t *set);//Initialize the signal set pointed to by set, and clear the corresponding bits of all signals in it, indicating that the signal does not contain any valid signals
int sigfillset(sigset_t *set);//Initialize the signal set pointed to by the set and set the corresponding bits of all the signals in it, indicating that the valid signals of the signal set include all the signals supported by the system
int sigaddset (sigset_t * set, int signo);
int sigdelset (sigset_t * set, int sign);
int sigismember (const sigset_t * set, int sign)
The return values ​​of these four functions all return 0 for success and -1 for failure;
sigismember is a boolean function used to determine whether a certain signal is included in the valid signals of a signal set

Note : Before using variables of type sigset_t, be sure to call sigemptyset or sigfillset for initialization, so that the signal is in a certain state.

sigprocmask (call this function to read or change the signal mask word (blocking signal set) of the process)

#include <signal.h>
int sigprocmask(int how,const sigset_t *set,sigset_t *oset);
Return value: 0 for success, -1 for failure

sigpending

#include <signal.h>
int sigpending(sigset_t *set);
Read the pending signal set of the current process, return 0 on success, -1 on error

example:

    #include <stdio.h>
    #include <signal.h>
    #include <unistd.h>
    
    int printsigset(sigset_t *set)
    {
        int i = 0;
        for(;i<32;i++){
            if(sigismember(set,i)){ //Determine whether the specified signal is in the target set
               putchar('1');
            }else{
                putchar('0');
            }
        }
        puts("");
    }
    
    intmain()
    {
        sigset_t s,p;
        sigemptyset(&s); //Define the signal set object and clear the initialization
        sigaddset(&s,SIGINT);
        sigprocmask(SIG_BLOCK,&s,NULL); //Set the blocking signal set and block the SIGINT signal
        while(1){
            sigpending(&p); //Read pending signal set
            printsigset(&p);
            sleep(3);
        }
        return 0;
    }

Test Results:


Since the SIGINT signal is blocked, pressing Ctrl+C will leave the SIGINT signal pending, and pressing Ctrl+\ can still terminate the program because the SIGQUIT signal is not blocked.


Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326974457&siteId=291194637