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
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.