Linux from Practice to Ascension No. 24 Signals in Linux

Time:2024-3-29
My Column
C from 0 to 1
Explore C++
Data structures from 0 to 1
Exploring Linux
Rookie Brush Up Collection
Welcome to follow: Like Favorite Leave a comment 🏇Code words is not easy, your Points of praise Favorites Follow Follow is really important to me, there are questions can be raised in the comments section, thanks for reading!

Article Catalog

preamble

This post will talk about signals in Linux, what are signals? How are signals generated? How are signals transmitted? How to capture signals? When you read this article, you will get the answers!

1 Signal volume

Let’s get a sense of semaphores. When we watch a movie, there are seats inside the theater (a resource inside the projection hall) right? And when does this seat really belong to me? Is it true that this seat belongs to me only if I sit in it myself? Not really! We need to buy a ticket first, and as soon as I do, I own the seat. The essence of our ticket purchase is a reservation for a seat. So corresponding to the system when a process wants to access a critical resource, can it access it if it wants to? If it’s true that you can access it if you want to, then there’s now such a thing: { A prior knowledge is needed before speaking: The CPU goes through three steps when executing instructions: 1. load data from memory into registers within the cpu 2. Execution of instructions 3. Write the result after CPU execution back to memory [All simplified here] } Suppose a variable is known to control the amount of some resource, suppose it is 5
  1. If process A wants to make use of this resource, then it needs to make 5, minus one.
Just as A loads 5 into memory, it is interrupted (perhaps because the time slice has run out or for some other reason), at which point it needs to save the scene, wait for it to be restored, and then continue on to the next step, which is now worth 4
  1. At this point, process B also comes to use this resource, so it is necessary to do the same as above, so that the remaining number of minus one, but the number of resources is still 5, because process A has not yet had time to write back into memory, so it is again minus one, the remaining 4 to write back into memory
  2. Process A then resumes the scene and begins to continue the unfinished task by writing back to memory 4
  3. At this time the actual value of the resource is 5-1-1=3, but because process A does not know what B has done, the number of resources at this time is again correct, in the subsequent process want to use this resource will find that the record is still there, but can not use this resource!
The above is a case of access, which tells us that it is not a matter of being able to go to an access if we want to, or being interrupted while accessing it is not a matter of being able to go to an access. Some processes like this need to be done in one go and cannot be interrupted, in the operating system it’s called atomicity There’s more.critical massandcritical areaconcepts critical massin other wordsA resource that only one process is allowed to use in a time period. Processes need to access critical resources mutually exclusive. (indicates contrast)critical areaThen it can be understood asThe piece of code that accesses the critical resource. the reason whycritical areaIt’s divided intokernel program critical area (computing)andOrdinary critical areas Critical resources accessed from the kernel program critical area may affect other management tasks in the operating system kernel if they are not released as soon as possible. Therefore, scheduling and switching are not allowed during kernel program critical areas access. Critical resources accessed from the normal critical area do not directly affect the management work of the operating system kernel. Therefore, scheduling and switching can be performed when accessing a normal critical zone. After all this time we haven’t talked about what a semaphore is, it’s obvious, it’s that variable ahaha It is.A variable used to manage resources Signals need to be requested before a process can access a critical resource. If the semaphore request is successful, the critical resources are reserved for the process. The essence of requesting a semaphore is the reservation of a critical resource And it has to be atomic, it can’t be interrupted

2 What is a Signal

2.1 Signals in life

  • You have bought many items online and are waiting for the delivery of different items to arrive. But even if the delivery doesn’t arrive, you know what you should do with the delivery when it comes. That is, you can “Identification of couriers
  • When the courier arrives downstairs, you receive a notification that the courier has arrived, but you are playing a game, and it will take 5min before you can pick up the courier. Then in this 5min, you did not go down to pick up the courier, but you are aware of the arrival of the courier. That is, the act of picking up the courier does not have to be carried out immediately, can be interpreted as “pick up at the right time”. There is a window of time between when you receive the notification and when you get the delivery. During this time, you don’t get the delivery, but you know that a delivery has arrived.Essentially you “remember there’s a delivery to pick up” and when you’ve timed it right and gotten it, it’s time to start processing it.
  • And there are three general ways to handle express delivery: 1.Execute the default action(Happy to open the delivery and use the merchandise) 2.Execute customized actions(The delivery is snacks that you are going to give to you your girlfriend) 3.Ignore the courier(After the courier brings it up, throw it off the bed and go ahead and start a game)
  • The whole process of delivery is asynchronous for you, you can’t tell exactly when the courier will call you.
From the aboveThe essence of a signal is a notification mechanism, the user or the operating system can be used by sending theCertain signals.The notification process.The occurrence of certain eventsThis can be followed up. And we learn some conclusions from the above example:
  • For a process to process a signal, it must have the ability to recognize the signal (see + process)
  • Signal generation is random, and the process may be busy with its own business, so subsequent processing of the signal may not be immediate
  • The corresponding signal is temporarily recorded on the process side for subsequent processing
  • When to deal with it? When it’s appropriate.
  • In general, signal generation is asynchronous for the process, and the process is not sure when the signal is generated

2.2 The concept of signaling

Signals are a way of asynchronous notification of events between processes and are soft interrupts.
The generation to the end of a signal can be roughly abstracted as a process like this Linux from Practice to Ascension No. 24 Signals in Linux

3 Before Signal Generation

3.1 Prior knowledge

3.1.1 Common approaches to signal processing
Before we get into how ctrl c is interrupted, let’s talk a little bit about it. As previously mentioned there are three common approaches to signal processing:
  • Default handling (process self-contained)
  • Ignore (receive a signal but ignore it)
  • Customized actions (capturing signals)
A more detailed statement is:
  1. Ignore this signal.
  2. Performs the default processing action for this signal.
  3. Providing a signal handler that requires the kernel to switch to the user state to execute this handler when processing the signal is called capturing (Catch) a signal.
The default signals in the operating system can be accessed through thekill -lferret out Linux from Practice to Ascension No. 24 Signals in Linux
  • In this, 1Signal 31 is a normal signal, 34Signal #64 is a real-time signal, i.e., a signal that requires immediate processing, and is only discussed in this article as an ordinary signal.
  • Each signal has a number and a macro definition name, which can be found in signal.h, e.g. where it is defined #define SIGINT 2
Linux from Practice to Ascension No. 24 Signals in Linux
  • The conditions under which each of these signals is generated, and what the default processing action is, are detailed in signal(7): man 7 signal
Linux from Practice to Ascension No. 24 Signals in Linux utilizationman 7 signalWe found that the default action of SIGINT is to terminate the process, and the default action of SIGQUIT is to terminate the process and Core Dump, now let’s verify it. Linux from Practice to Ascension No. 24 Signals in Linux
3.1.2 Understanding Core Dump
So what is Core Dump?
When a process is terminated abnormally, you can choose to save all of the process’s user-space memory data to disk, usually in a file called core, which is called Core Dump. The process is usually terminated abnormally because of a bug, such as an illegal memory access that caused a segmentation error, and you can use a debugger to examine the core file afterwards to find out the cause of the error, called Post-mortem Debugging. This is called Post-mortem Debug. How many core files a process is allowed to generate depends on the process’s Resource Limit (this information is stored in the PCB). By default, core files are not allowed to be generated because they may contain sensitive information such as user passwords, which is not secure. This limit can be changed with the ulimit command during the development and debugging phase to allow the generation of core files. First, use the ulimit command to change the Resource Limit of the Shell process to allow core files up to 1024K.ulimit -c 1024 Linux from Practice to Ascension No. 24 Signals in Linux
Then write a dead loop program Linux from Practice to Ascension No. 24 Signals in Linux
Run it. Linux from Practice to Ascension No. 24 Signals in Linux What can be noticed is that both ctrl c and ctrl \ can be interrupted, but ctrl \ will do a core dump, generating the file core.22533. The ulimit command changes the Resource Limit of the Shell process. The PCB of the test process is copied from the Shell process, so it has the same characteristics as the Shell process. The same Resource Limit value is used to generate the Core Dump.
Some of the problems you may run into are: Can’t see the core dumped files in the current directory At this point, you’ll need to look at the directory of the core dump and make changes View:cat /proc/sys/kernel/core_pattern Modification:echo core > /proc/sys/kernel/core_pattern Modifications require root privileges
Using core files You need to use the command under gdbcore-file [core. process ID] Below: Linux from Practice to Ascension No. 24 Signals in Linux
core dumpMainly appears in: the process of some kind of abnormality, whether the operating system to speak the current process in the memory of the relevant core data to disk, its main application scenario is the debugging of the program crash, used to find the cause of the crash.
Why is core dump normally turned off in production environments? Turning off core dump in production environments can improve the security and stability of your system. Here are some reasons why:
  1. Security Issues: core dump is a file in which the operating system dumps the memory of a process to disk, containing all the information when the program is running. If sensitive information is written to the core dump file, such as passwords, private keys, etc., then this information can be stolen by hackers. Turning off core dump prevents this from happening.
  2. Stability issues: In some cases, core dumps may cause IO and CPU problems, affecting the stability and availability of the system. For example, if core dump occurs frequently, it may lead to insufficient disk space and the need to clean up old core dump files. In addition, generating large core dump files can also take up system resources, thus affecting other running processes.
  3. Debugging tools: In production environments, debugging is usually not necessary because the code is already fully tested and verified. Turning off core dump prevents the core dump file from being used illegally to analyze code or for debugging, thus increasing system security.
To summarize, turning off core dump in the production environment can improve the security and stability of the system, avoiding data leakage and system stability problems. If you need to debug, you can turn on core dump when needed.

3.2 Signal generation via terminal keys

3.2.1 ctrl c
Take the following program as an example:
#include<iostream>
using namespace std;
int main()
{
    while(1)
    {
        cout<<"my linux"<<endl;
    }
    return 0;
}
That’s right! This program is a dead end! How do we break after running it on the command line in general? ctrl c Linux from Practice to Ascension No. 24 Signals in Linux After pressing ctrl c, the process is terminated. Linux from Practice to Ascension No. 24 Signals in Linux Right, but why does pressing ctrl c terminate the loop? This actually sends signal #2 to the operating system. How do I prove it? Here you need to use thesignalfunction (math.)
3.2.1.1 signal function
sighandler_t signal(int signum, sighandler_t handler);
  • Role: Modify the process default action on signals.
  • Return Value: Its return value is a function pointer to the previous handler (callback) function for this signal, or SIG_ERR.
  • Parameters:
    • int signum Signal to be processed
    • sighandler_t handler Signal handler function to replace
We can then write the following code to verify that ctrl c is not sending signal #2
#include<iostream>
#include<unistd.h>
#include<signal.h>

void handler(int signo)
{
    printf("get a signal: signal no is:%d",signo , getpid());
}


int main()
{
    signal(2,handler);

    while(1)
    {
    	printf("hello world! my pid is : %d\n" , getpid());
        sleep(1);
    }
            
    return 0;
}
What this code means is that it prints a sentence every second and prints the pid of the process And signal #2 was registered using the signal function Caution: Registering to Signal #2 does not mean executing Signal #2, for example, if your teacher tells you to go to class when the bell rings, but doesn’t tell you to go to class, just teaches you what to do when Here are the results of the run: Linux from Practice to Ascension No. 24 Signals in Linux What we’ve found is that when we press ctrl + c now, the process doesn’t exit, it prints out the statement we’ve registered. And since we’re registering signal number 2, that’s proof that ctrl + c is actually sending signal number 2 to the process.
3.2.2 How do you understand that the ctrl c key combination becomes a signal?
That is to say how can it terminate the process? Because the keyboard passes through theinterrupt methodwork, it means that the communication between the keyboard and the computer isasynchronous. When a key on the keyboard is pressed, the keyboard sends this keystroke information to the computer. The computer will respond to the keyboard after receiving this information. This improves the speed of the computer’s response and makes communication between the keyboard and the computer more efficient. Simply put, the interrupt method of the keyboard works like a process of “key pressed – message sent – computer receives and processes”. So it recognizes the key combination ctrl c and the operating system goes and interprets the key combination, what it represents and then goes and looks up the list of processes and finds the process that’s running in the foreground, the process that’s where you pressed ctrl c, and the operating system writes the corresponding signal.
3.2.3 How do you understand that a signal is saved by a process?
Since the process can temporarily ignore the signal and still follow up with another operation, it also means that the process has a “east and west“What is it that saves the signal? What would be the structure? As previously mentionedThere are 31 common signals, and there are only two states of a signal: happening, not happening, so it is only necessary to store the signal as a bitmap, so what the operating system is actually doing by writing the corresponding signal as described before is modifying the bitmap. (indicates contrast)The nature of signalingThat is:The operating system writes signals to the target process, i.e., modifies the specified bitmap structure in the PCBThat’s what completes the process of sending
3.2.4 Front-end and back-end operation
Signals sent via the keyboard only have an effect on the program running in the foreground To run the program in the background you need to signal it with the kill instruction validate (a theory) Linux from Practice to Ascension No. 24 Signals in Linux

3.3 Calling the system interface to signal a process

First execute the dead loop program in the background and then send it the SIGSEGV signal with the kill command.
kill -SIGSEGV [pid]
Linux from Practice to Ascension No. 24 Signals in Linux
  • 24548 is the id of the mycode3 process, and the reason why you have to enter again before displaying the (segmentation fault) Segmentation fault is because before the 24548 process was terminated, it had already returned to the Shell prompt and was waiting for the user to enter the next command, and the Shell didn’t want to interleave the Segmentation fault message with the user’s input, so it waited for the user to enter the command before displaying the Segmentation fault message. The Shell does not want the Segmentation fault message to be interleaved with the user’s input, so it waits for the user to enter the command before displaying it.
  • The kill command, which specifies that a certain signal be sent, can be written in a variety of ways. The above command can also be written as kill -SIGSEGV 24548 or kill -11 24548, where 11 is the number of the signal SIGSEGV. Segment errors have been encountered in the past due to illegal memory accesses, but the program itself is correct, and sending SIGSEGV to it can also generate segment errors.
The kill command is implemented by calling the kill function.
3.3.1 The kill function
The kill function sends a specified signal to a specified process.
#include <signal.h>

int kill(pid_t pid, int sig);
//pid_t pid: specify the process
//int sig: specified signal
//Success returns 0, error returns -1.
usage example
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>

int main()
{
    int count = 3;
    while(1)
    {
    	if (count == 0)
    	{
      		kill(getpid() , 9);                                                                
    	}
        printf("my pid is: %d\n",getpid());
        sleep(1);
        count--;
    }
    return 0;
}
The above code will be killed by signal 9 sent by the kill command after printing the process’s pid three times The results of the run are as follows Linux from Practice to Ascension No. 24 Signals in Linux
3.3.2 raise function
The raise function can send a specified signal to the current process (signaling itself).
#include <signal.h>

int raise(int sig);
//Parameter is the signal to be sent
//Success returns 0, error returns -1.
usage example
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>

int main()
{
    printf("my pid is %d\n", getpid());
    sleep(3);
    raise(9);
    return 0;
}
This code is meant to send a signal 9 to itself after three seconds of hibernation The results of the run are as follows Linux from Practice to Ascension No. 24 Signals in Linux
3.3.3 abort function
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.
//Send SIGABRT signal to yourself
//Additionally, the SIGABRT signal cannot be trapped, and the process must terminate abnormally if the abort function is called.
Example of use:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>

int main()
{
    printf("my pid is %d\n", getpid());
    sleep(3);
    abort();
    return 0;
}
What this code means is that after three seconds of hibernation, it sends a message to itself.SIGABRTsignal running result Linux from Practice to Ascension No. 24 Signals in Linux
3.3.4 How to Understand the System Call Interface
The user calls the system interface → the program executes the corresponding system call code of the operating system → the operating system extracts parameters or sets specific values → the operating system writes a signal to the target process → it modifies the signal flag bit of the corresponding process → the process subsequently processes the signal → it performs the corresponding processing action.

3.4 Signal generation from software conditions

3.4.1 alarm function
I’ve talked about this phenomenon before in the pipeline section of process communication: When two processes are communicating, if we close the read side, the write side will be closed automatically, and this is the time when the operating system sends signal 13 to the write sideSIGPIPE (Explain this phenomenon, because writing data into a pipe when no one is reading it is actually a waste of resources, and the operating system, as the manager of the resources, won’t allow it.) And now to introduce signal #14 which is also generated by software First a function is introduced:alarmfunction (math.) We can call this function to generate an alarm that tells the system to send signal 14 to the process after a certain amount of time, and it has the following prototype.
#include <unistd.h>

unsigned int alarm(unsigned int seconds);
//Parameter: is an unsigned integer that indicates how many seconds to set the
Its return value is also an unsigned integer in two ways
  • Returns 0 if the process has not set an alarm before.
  • If the process has set an alarm in between, the return value is the time left on the previous alarm and the current alarm overwrites the previous alarm.
Let’s use a real life example to explain this concept Let’s say we want to take a nap in the middle of the day and we set an alarm for 30 minutes. And after 20 minutes we woke up, looked at the alarm clock and had 10 minutes of sleep left. But we wanted another 15 minutes of sleep, so we set another 15-minute alarm to override the last one. usage example
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>

int main()
{
    int count = 0;
    alarm(1);
    while(1)
    {
        printf("count is :%d\n", count++);
    }
    
    return 0;
}
This code is meant to count how many count statements can be printed in one second running result Linux from Practice to Ascension No. 24 Signals in Linux About 410,000 times. Now, let’s change our thinking and capture signal number 14 and have it print what the count is when it ends instead of printing it in the while loop.
Here is the code to remove the line numbers and optimize the formatting:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>

long long count = 0;

void handler(int signo)
{
    printf("count is : %d\n", count);
    exit(0);
}

int main()
{
    signal(14, handler);
    alarm(1);
    while(1)
    {
        count++;
    }
    
    return 0;
}
running result Linux from Practice to Ascension No. 24 Signals in Linux This time it’s straight out of the box.long long The range of what can be expressed, becomes negative Why? As we’ve explained in Basic IO, the cpu is very fast and the peripherals are very slow, so the count value that is printed is much smaller than the count value that is not printed.
3.4.2 How to Understand Software Conditions to Signal a Process
  • The operating system recognizes that a software condition is triggered or not met
  • The operating system builds a signal to send to the specified process

3.5 Hardware Abnormal Generation Signal

Hardware exceptions are detected by the hardware in some way by the hardware and notified to the kernel, which then sends appropriate signals to the current process.
3.5.1 Dezero exceptions
For example, if the current process executes an instruction that divides by 0, the CPU’s arithmetic unit generates an exception, which is interpreted by the kernel asSIGFPEsignals are sent to the process.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void handler(int signo)
{
    printf("signo is : %d\n", signo);
}

int main()
{
    int i = 0;
    for (i = 0; i < 32; i++)
    {
        signal(i, handler);
    }

    int a = 100;
    a /= 0;
    return 0;
}
running result Linux from Practice to Ascension No. 24 Signals in Linux The process will keep printing signal 8 into the screen
3.5.2 Wild Pointer Exceptions
Then again, if the current process accesses an illegal memory address,, the MMU generates an exception which the kernel interprets asSIGSEGVsignals are sent to the process.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void handler(int signo)
{
    printf("signo is : %d\n", signo);
}

int main()
{
    int i = 0;
    for (i = 0; i < 32; i++)
    {
        signal(i, handler);
    }

    int* p = NULL;
    *p = 100;

    return 0;
}
We capture all signals, customize their handling, and intentionally write a null pointer access running result Linux from Practice to Ascension No. 24 Signals in Linux We can hit the kill -l command to see all the signals Linux from Practice to Ascension No. 24 Signals in Linux After that, we’ll remove the signal-capture part of the code, leaving only the divide-by-0 operation. Compile and run to see the results Linux from Practice to Ascension No. 24 Signals in Linux We’ve found that the process will simply terminate and report an FPE error. So, here’s what we know. In Linux, the essence of a process crash is that the process receives the corresponding signal, and then the process performs the signal’s default action (kills the process).
3.5.3 How to Understand the Division by 0 Exception
We all know that the calculation is carried out by the CPU hardware, and there are registers inside the COU, in the registers there is a kind of status register (principle: bitmap), the status register will have the corresponding status flag bit, including overflow flag bit, etc., the operating system will automatically carry out the detection after the calculation is completed. In terms of the overflow flag bit, the operating system recognizes the overflow flag bit if it is 1, which means there is an overflow problem, immediately finds out which process is currently running, extracts the PID, and the operating system completes the process of signaling the process to be handled at the right time. Does the process always exit when there is a hardware anomaly? Not necessarily, it usually quits by default, but we can’t do much without it. Why is it a dead loop? The exception in the register was never resolved.
3.5.4 How to understand wild pointers or out-of-bounds problems
In our program runtime we use the address to find the target location. And all the addresses in our language are virtual addresses. And the virtual address will go through the page table and MMU (Memory Manager Unit hardware) and then mapped to the physical address And the essence of dereferencing a null pointer is accessing an illegal address At this point the MMU hardware reports an error in the CPU’s operations When an error occurs, the operating system looks for the process that triggered the error. When the operating system finds the process, it sends a signal to the process to kill the faulty process.
3.5.5 Causes of process crashes
As we learned earlier in Process Control, processes can be terminated in one of two ways, depending on whether they exit normally or not. One is when all the code has been run and the program exits on its own, and the other is when the program terminates abnormally. Previously, we explained the waitpid function in process control, which has an output parameter called status. The lower seven bits of this function are our termination signals. Linux from Practice to Ascension No. 24 Signals in Linux In the previous article we talked aboutcore dumpRelevant knowledge that will come in handy now! Let’s set it up in space first Linux from Practice to Ascension No. 24 Signals in Linux Then run it again Linux from Practice to Ascension No. 24 Signals in Linux core dumpedThe error message representing our crashed process is saved And then also found an extra one here calledcore.28652 documents So what’s the point of this file? We can debug this executable using gdb After that, type core-file + core dump file Below we can see the various error messages of the file including the termination signal received, the number of erroneous lines and so on. We call this type of debuggingdebugging after the event Linux from Practice to Ascension No. 24 Signals in Linux
3.5.6 How the operating system is signaled
We need a space in the process’s structure to hold the signals we receive. Why is that? Remember our example above of the alarm clock going off but you want to go back to sleep? That means that at the time the signal is being sent, we may have something more important (or higher priority in the eyes of the computer) to do, so the signal has to be held in abeyance. So how should signals be saved? We use the kill -l command to see all the signals Linux from Practice to Ascension No. 24 Signals in Linux As we can see, the numbering of the signals here is actually very regular, it’s numbered from 1 to 31. When we see such a regular number, we can always think of an array subscript. Of course it’s perfectly feasible to use the subscripts of an array to hold signals But here we only need to know if the signal exists and nothing else, so it’s better to use bits to signal the existence of a signal. It is also true that a 32-bit unsigned integer is actually used in linux systems to flag the presence or absence of each signal The conceptual diagram is as follows Linux from Practice to Ascension No. 24 Signals in Linux We’ll define the lowest position as the first, and we’ll increment from there. If the bit of this bit is 1 it means the signal is received If the bit of this bit is 0 it means the signal is not received Now let’s understand how the operating system sends signals. Essentially, the operating system takes the signal bitmap in the PCB and sets it to 1 for location. So that means that our current understanding of the signals sent by the operating system should be that the operating systemwrite signal

3.6 Concluding thoughts

All of the signal generation described above ultimately requires an OS to perform it, why? All signals are generated and ultimately executed by the operating system (OS), this is because the OS is the manager of the process. The OS is responsible for scheduling and controlling the execution of processes, including handling signals.
Is the signal processed immediately? When appropriate, the processing of signals is usually asynchronous, i.e., signals can occur and be delivered to the process at any time. Upon receiving a signal, the process, depending on the type of signal and the current state of the process, can choose to either process the signal immediately or log the signal temporarily and wait for the right time to process it.
Do signals need to be temporarily logged by the process if they are not being processed immediately? Where is the most appropriate place to record it? If the process does not process the signal immediately, the operating system records the signal information in the process’s process control block (PCB). The process control block is a data structure used by the operating system to manage the process and contains the state, context, and other relevant information about the process. The purpose of logging signals is to ensure that the process can be notified at the appropriate time that it has an unhandled signal that needs to be processed.
Can a process know, when it has not received a signal, what it should do with a legitimate signal? A process cannot know what it should do with a legitimate signal until it receives it. A process can only choose what to do with a signal when it receives it, based on the type of signal and its own state. Nor can a process determine whether it will receive a particular signal until the signal is delivered to the process. When a process receives a signal, it can either handle it with a signal handler function or with the default handling action. If the process does not set a customized signal handler function, it will execute the default handling action of the signal. The default processing action is specified by the operating system, and different types of signals may have different default processing actions. Note that even if a process sets a custom signal handler, there is no guarantee that the handler will be executed. For example, if the process is blocking a system call, the handler may not be executed immediately when the signal arrives, but will wait until the system call returns. Therefore, a process cannot know in advance how to handle a legitimate signal before it receives it. It can only choose how to handle a signal after receiving it, depending on the type of signal and its own state.
How do you understand OS sending signals to a process? Can you describe the complete sending process? The complete send processing is roughly as follows:
  1. The operating system receives the signal and determines which process to send it to.
  2. The operating system checks how the target process handles the signal.
  3. If the process ignores the signal, the operating system does nothing.
  4. If the process sets up a custom signal handler function, the operating system passes the signal to that handler function.
  5. The handler function performs the relevant operations and can modify the signal handling or other states as required.
  6. If the process does not set a custom signal handler function and the signal has a default handling action, the operating system performs that default handling action.
  7. After processing the signal, control is returned to the target process to continue executing the original program.

4 In signal generation

4.1 Basic Concepts: Signal Delivery, Signal Pending, Blocking Signal, Ignore Signal

There are a few basic concepts that we need to understand when looking at signal generation:
  • The actual execution of the signal processing action is called signal delivery (Delivery)
  • The state of a signal between its generation and delivery is called signal pending (Pending).
  • A process can choose to block/block a signal.
  • A blocked signal remains pending until the process unblocks the signal, then it performs the uploaded action.
  • Note that blocking and ignoring are different in that a signal will not be delivered as long as it is blocked, while ignoring is an optional processing action after delivery.
How do I understand it?
Let’s take this example, let’s say you set an alarm to wake up tomorrow morning, it will remind you to get up for class, and by the next day, it will be one of these things:
  • You hear the alarm and get up for class. That’s signaling. That’s the default action.
  • The state between the time you hear your alarm clock go off and the time you go to class is the signal pending
  • Your roommate hears the alarm and turns it off, causing you to go back to sleep without hearing it, which is signal blocking/blocking
  • You hear the alarm go off but ignore it and go back to sleep, that’s signal neglect.
  • You hear the alarm go off and then get up and turn it off and do something else instead of going to class, that’s performing a custom action.
  • The difference between blocking and ignoring comes down to whether or not you receive the signal; blocking is when you don’t receive it, and ignoring is when you receive it and don’t process it.

4.2 Structure of signals in the kernel > block bitmap pending bitmap handler function table

Schematic representation of signals in the kernel: Linux from Practice to Ascension No. 24 Signals in Linux They are block bitmap pending bitmap handler function table I’ll cover them all below
Block Bitmap
The block bitmap marks whether the signal is blocked or not. 1 indicates that the bitmap is blocked 0 indicates that it is not blocked
pending bitmap
The pending bitmap flags whether or not this signal is received. 1 indicates that the signal is received 0 indicates that the signal is not received
Handler function table
Inside the handler function table are the addresses of the various functions that are handled when the signal is accepted. We use these addresses to call functions Now, to answer a previous question, why is it that even if we don’t see a stoplight, we know that it’s red or green? Essentially, it’s because there’s a stoplight function registered in the handler’s function table. Linux from Practice to Ascension No. 24 Signals in Linux
  • Each signal has two flag bits indicating blocking and pending, and a function pointer indicating the processing action.
  • When a signal is generated, the kernel sets the pending flag for that signal in the process control block and does not clear the flag until the signal is delivered.
  • In the example above, the SIGHUP signal is not blocked nor generated, and the default processing action is performed when it is delivered.
  • The SIGINT signal was generated, but is being blocked, so it cannot be delivered at this time. Although its processing action is to be ignored, the signal cannot be ignored until the block is lifted, because the process still has a chance to change its processing action before lifting the block.
  • SIGQUIT signal has not been generated, once generated SIGQUIT signal will be blocked, its processing action is a user-defined function sighandler.
  • What happens if a signal is generated multiple times before the process unblocks it?
  • The system is allowed to deliver the signal one or more times.Linux is implemented in such a way that regular signals that are generated multiple times before being delivered are counted only once, whereas real-time signals that are generated multiple times before being delivered can be placed sequentially in a queue. Real-time signals are not discussed in this chapter.

4.3 sigset_t type

In fact, in our operating system, in addition to types like int double, there are other types that the operating system gives us. Like a key_t type in shared memory. To solve the problem of pending and blocking bits the operating system provides us with a bitmap-like data type sigset_t And from the above figure, each signal has only one bit of pending flag, either 0 or 1, without recording how many times the signal has been generated, and the blocking flag is also expressed in this way. Thus, the pending and blocking flags can be of the same data typesigset_tto store.sigset_tis called a signal set. It is implemented in linux in the following way:
#define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int)))
typedef struct
{
	unsigned long int __val[_SIGSET_NWORDS];
} __sigset_t;

typedef __sigset_t sigset_t;
This type can indicate that each signal is **”valid”or“Invalid “** status.
  • The meaning of “valid” and “invalid” in the blocking signal set is whether the signal is blocked or not.
  • The meaning of “valid” and “invalid” in the set of pending signals is whether the signal is pending or not.
The next section describes in detail the various operations on signal sets. Blocking signal set is also called the current process of the signal mask (Signal Mask), where the “blocking” should be interpreted as blocking rather than ignoring.

4.4 Signal Set Manipulation Functions

The sigset_t type uses one bit for each signal to indicate the “valid” or “invalid” state. How these bits are stored inside the type depends on the system implementation and is of no concern from the user’s point of view. The user can only call the following functions to manipulate the sigset_ t variable and should not interpret its internal data in any way, e.g. printing the sigset_t variable directly with printf is meaningless. The operating system provides the following functions for us to manipulate the signal set
#include <signal.h>

int sigemptyset(sigset_t *set);

int sigfillset(sigset_t *set);

int sigaddset(sigset_t *set, int signum);

int sigdelset(sigset_t *set, int signum);

int sigismember(const sigset_t *set, int signum);
function explanation
  • sigemptyset function: initialize the signal set pointed to by set, so that the corresponding bit of all the signals in it is cleared to zero, indicating that the signal set does not contain any valid signals.
  • sigfillset function: initialize the signal set pointed to by set, so that the corresponding bit of all signals in it is set, indicating that the valid signals of the signal set include all signals supported by the system.
  • sigaddset function: adds a valid signal to the set of signals pointed to by set
  • sigdelset function: removes a valid signal from the set of signals pointed to by set.
  • The sigemptyset, sigfillset, sigaddset and sigdelset functions are allSuccess returns 0. Error returns -1.
  • sigismember function: determines whether a signal is contained in the set of signals pointed to by set If it is contained, return 1 If it is not contained, return 0 Failed call returns -1

4.5 sigprocmask Function

Calling the function sigprocmask allows you to read or change the signal mask word (blocking signal set) of a process.
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
Return Value:
  • 0 if successful, -1 if wrong
Parameter Description:
  • The how parameter is usually represented by a macro, which indicates the mode of the setting.
  • If oset is a non-null pointer, the current signal mask word of the read process is passed through the oset parameter.
  • If set is a non-null pointer, changes the signal mask word of the process, with the argument how indicating how to change it.
  • If oset and set are both non-null pointers, backup the original signal mask word into oset first, and then change the signal mask word according to the set and how parameters.
  • Assuming the current signal mask word is mask, the following table describes the optional values for the how parameter. | Options | Meaning | | — | — | | SIG_BLOCK | set contains the signals we wish to add to the current signal mask word, equivalent to mask=mask | set | | SIG_UNBLOCK | set contains the signal we wish to unblock from the current signal mask word, equivalent to mask=mask && ~set | | SIG_SETMASK | Sets the current signal mask word to the value pointed to by set, equivalent to mask=set |
Note: If a call to sigprocmask unblocks several currently pending signals, deliver at least one of them before sigprocmask returns. That means that if we had blocked signal number two, and now we unblock it, the process will immediately receive signal number two and execute the registration function. Why we get a signal as soon as we unblock the process is something we’ll explain in more depth later.
4.5.1 Signals that cannot be blocked (phenomena)
We can block signals using the sigprocmask we just learned about Here’s a code to try blocking signals #2 and #9
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>

int main()
{
    sigset_t iset;

    sigemptyset(&iset);
    sigaddset(&iset , 2);
    sigaddset(&iset , 9);
    sigprocmask(SIG_SETMASK , &iset , NULL);

    while(1)
    {
        printf("hello world , my pid is:%d\n",getpid());                                                
        sleep(1);
    }
    return 0;
}
After running we used ctrl c to terminate the program and found that it was not working Linux from Practice to Ascension No. 24 Signals in Linux We’ll restart a process and send it a signal 9. Linux from Practice to Ascension No. 24 Signals in Linux We find that the process is killed outright at this point But we’re blocking signal 9 up there. Based on the above phenomenon, we can infer that signal number 9 cannot be blocked.

4.6 sigpending function

The sigpending function gets the set of pending signals for a process. The function prototype is as follows
int sigpending(sigset_t *set);
Return Value: Returns 0 if the function call succeeds and -1 if it fails. Parameters: The argument to this function is an output parameter. We can only use this function to get the pending bitmap of the process, but not to send signals directly to the process (the way to send signals has already been described, so I won’t go into it again). Next, let’s do a simple experiment: let’s see what it looks like in the pending bitmap when we send a signal The experimental code is as follows
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void printPending(sigset_t *pending)
{
	int i = 1;
	for (i = 1; i <= 31; i++){
		if (sigismember(pending, i)){
			printf("1");
		}
		else{
			printf("0");
		}
	}
	printf("\n");
}
int main()
{
	sigset_t set, oset;
	sigemptyset(&set);
	sigemptyset(&oset);

	sigaddset(&set, 2); //SIGINT
	sigprocmask(SIG_SETMASK, &set, &oset); //Obstruction No. 2 signal

	sigset_t pending;
	sigemptyset(&pending);

	while (1){
		sigpending(&pending); // Get pending
		printPending(&pending); //print the pending bitmap (1 for pending)
		sleep(1);
	}
	return 0;
}
Explain the code above We’ll block signal 2, and every second after that, we’ll print a bitmap of the current process pending. The result after running is as follows Linux from Practice to Ascension No. 24 Signals in Linux We find that after receiving the second signal the position 2 of the pending bitmap becomes 1 If we want to observe the change of signal position 2 from 1 to 0, simply unblocking it will not work. Because the default handling of signal 2 is to let the process exit, so if we want to observe signal 2 going from 1 to 0, we need to capture signal 2, which is represented in the code as follows
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void printPending(sigset_t *pending)
{
	int i = 1;
	for (i = 1; i <= 31; i++){
		if (sigismember(pending, i)){
			printf("1");
		}
		else{
			printf("0");
		}
	}
	printf("\n");
}
void handler(int signo)
{
	printf("handler signo:%d\n", signo);
}
int main()
{
	signal(2, handler);
	sigset_t set, oset;
	sigemptyset(&set);
	sigemptyset(&oset);

	sigaddset(&set, 2); //SIGINT
	sigprocmask(SIG_SETMASK, &set, &oset); //Obstruction No. 2 signal

	sigset_t pending;
	sigemptyset(&pending);

	int count = 0;
	while (1){
		sigpending(&pending); // Get pending
		printPending(&pending); //print the pending bitmap (1 for pending)
		sleep(1);
		count++;
		if (count == 20){
			sigprocmask(SIG_SETMASK, &oset, NULL); //restore the once-signaled mask word
			printf("Recovering signal mask word \n");;
		}
	}
	return 0;
}
Explain the code above This code blocks signal 2 at the beginning of the process and unblocks signal 2 after 20 seconds. Pending bitmaps are printed every second. The results of the demo are as follows Linux from Practice to Ascension No. 24 Signals in Linux

5 After signal generation

Let’s review the example explained between us, after the alarm clock went off, you got up, now you have to choose an appropriate time to go to class, this appropriate time is when you wash up, eat breakfast, and have to go to class before the bell rings. So what is this appropriate time for computers? The first thing we need to know is that signals are stored in the pending bitmap in the PCB of the process, and if we want to process them, we need to check if there’s a signal in the pending bitmap, if it’s blocked, and how the signal is handled. We’re going to jump right to a conclusion here. signal from**Kernel state**come (or go) back**User state**The above tests are performed and processed when the What is the kernel state and what is the user state? Let’s get to know them.

5.1 Kernel State and User State

Definition:
  • The kernel state is the privileged level at which the operating system kernel operates, with the highest privileges and access rights. In the kernel state, the operating system can directly access and control all hardware resources and core functions of the system, execute privileged commands and handle critical system tasks, such as managing processes, file systems, device drivers, and so on. The kernel state can perform operations that are potentially dangerous and affect system stability, so only the operating system kernel can enter the kernel state.
  • The user state is an execution mode in which an application program runs with restricted privileges. In the user state, the application program can only access restricted resources and execute restricted instructions, and cannot directly access and control the underlying hardware of the system. Applications running in the user state must request the kernel to provide privileged functions and services, such as process creation, file reading and writing, network communication, etc., through System Call. Applications in the userland cannot execute some key privileged instructions and perform system-level operations.
The difference between them is permissions, which are greater in the kernel state than in the user state. When does the transition between user state and kernel state occur? For example, a system call that uses the system’s interface goes from the user state to the kernel state, and a running program that has defects, traps, exceptions, etc. also goes from the user state to the kernel state. Can you be more specific? without doubt We know that when we run a user-written program, the operating system creates processes At this point there will be a data structure called PCB loaded into memory after mapping through the page table and mmu (hardware). So does the OS code and data have to be loaded into memory as well? Of course, the time our computers actually wait to boot up includes the time it takes for the operating system’s code and data to load into memory. So now we understand that the operating system and the user’s data and code are loaded into memory. So how are userland and kernelland distinguished? Their actual calling process is shown below Linux from Practice to Ascension No. 24 Signals in Linux What you can see is that the user and kernel code and data do not use the same page table. The operating system uses a kernel-level page table The user is using a user-level page table User space is understood to be a temporary variable that belongs to the current process. In kernel space, it’s a global variable that all processes see the same. From here we can draw a conclusion No matter how we switch processes, we find the same operating system code and data. So how do we understand process switching from the standpoint of the present? In effect, the current process enters the kernel state and finds the operating system’s data and code, then replaces the data and code in user space with the operating system’s privileges, and the process switch is complete. So what is the switch between user state and kernel state in a system roughly like? The approximate transformation is shown below Linux from Practice to Ascension No. 24 Signals in Linux It’s important to note that if the signal’s action is to terminate, then the current process doesn’t need to return to the user state to terminate. If it’s hard to remember we can abstract this diagram a bit more Linux from Practice to Ascension No. 24 Signals in Linux where how many intersections of the graph with the line in the center indicates how many switches there are The intersection in the middle of the graph indicates the moment of checking the PENDING table. Note that we are checking the pending table in kernel state, which means that the intersection of the graphs must be in kernel state. As we said earlier, the kernel state has higher privileges than the user state, so can we handle data directly in the kernel state without switching between the user state and the kernel state? Theoretically, it is possible. The kernel state has very high privileges and can execute various functions in the user state. But in practice, this is not possible because the kernel state has very high privileges, and if the user writes some dangerous code, such as deleting libraries, and executes it in the kernel state, it will have a very bad effect.

5.2 Capturing signals

5.2.1 How the kernel implements signal capture
If the processing action of a signal is a user-defined function, this function is called when the signal is delivered, which is called capturing the signal. Linux from Practice to Ascension No. 24 Signals in Linux Since the code for the signal processing function is in user space, the processing is more complex, as exemplified by the following. The user program registers the handler function sighandler for the SIGQUIT signal. The main function is currently executing, when theInterrupt or ExceptionSwitch to kernel state. After the interrupt has been processed toBefore returning to the main function in userlandThe signal SIGQUIT is checked. The kernel decides not to restore the context of the main function to continue execution after returning to the user state. Instead, it executes the sighandler function that The sighandler and main functions use different stack spaces, the There is no calling and called relationship between them, they are two separate control processes. The sighandler function returns and automatically executes the special system call sigreturn to enter the kernel state again. If there are no new signals to deliver, returning to the user state this time restores the context of the main function to continue execution.
5.2.2 sigaction function
In addition to capturing signals with the signal function used earlier, we can also use the sigaction function to capture signals. The sigaction function has the following prototype:
#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
Return Value:
  • The sigaction function reads and modifies the processing action associated with the specified signal. A successful call returns 0, an error returns – 1.
Parameters:
  • signo is the number of the specified signal.
  • If the act pointer is non-null, the processing action of the signal is modified according to act.
  • If the oact pointer is non-null, the original processing action of the signal is passed through oact.
  • act and oact point to the sigaction structure, which is defined as follows:
struct sigaction {
	void(*sa_handler)(int);
	void(*sa_sigaction)(int, siginfo_t *, void *);
	sigset_t   sa_mask;
	int        sa_flags;
	void(*sa_restorer)(void);
};
Let’s start with the first 4 members The first member of the structure sa_handler:
  • Assign sa_handler to the constant SIG_IGN passed to the sigaction function Indicates that the signal is ignored
  • Assign sa_handler to the constant SIG_DFL and pass it to the sigaction function to execute the system’s default action.
  • Assigning sa_handler as a function pointer indicates that a signal is captured with a custom function or that a signal handler function is registered with the kernel.
The second member of the structure sa_sigaction:
  • sa_sigaction is a real-time signal handler, so we don’t need to worry about it, just leave it blank.
The third member of the structure sa_mask:
  • The first thing to note is that when a signal handler is called, the kernel automatically adds the current signal to the process’s signal mask, and when the signal handler returns, it automatically restores the original signal mask. This ensures that when a signal is processed, if it is generated again, it will be blocked until the current process is complete.
  • If you wish to automatically mask additional signals in addition to the current signal when calling a signal processing function, use the sa_mask field to specify these additional signals to be masked. When the signal processing function returns, the original signal mask is automatically restored.
The fourth member of the structure sa_flags:
  • The sa_flags field contains a number of options Here it is straightforward to set sa_flags to 0
Let’s try to use it next: The code is as follows:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>

struct sigaction act, oact;
void handler(int signo)
{
	printf("get a signal:%d\n", signo);
	sigaction(2, &oact, NULL);
}
int main()
{
	memset(&act, 0, sizeof(act));
	memset(&oact, 0, sizeof(oact));

	act.sa_handler = handler;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);

	sigaction(2, &act, &oact);
	while (1){
		printf("I am a process...\n");
		sleep(1);
	}
	return 0;
}
We send the program a signal #2 and then have it restore it the second time it receives the Demonstration of the effect is as follows Linux from Practice to Ascension No. 24 Signals in Linux

5.3 Reentrant Functions

Let’s understand it through a chained table insertion example. The insert function is called in the following main function to insert node1 into a linked table. The insert function is also called in a signal processing function to insert node2 into a linked table. Linux from Practice to Ascension No. 24 Signals in Linux Here’s the list. Linux from Practice to Ascension No. 24 Signals in Linux Here’s how this chain table changes
  1. We call a function to do header insertion and send a signal at this point (which also uses the header insertion function) After the first step of header insertion, the process interrupts and enters the kernel state.
Linux from Practice to Ascension No. 24 Signals in Linux
  1. The kernel checks the signal and calls a custom handler to header-link the table (back to userland).
Linux from Practice to Ascension No. 24 Signals in Linux
  1. Go back to kernel state and check for no signals. Continue inserting to complete header insertion of the linked table. Go back to user state.
Linux from Practice to Ascension No. 24 Signals in Linux
  1. Continuing the interrupted header insertion of node1 results in the main function and sighandler successively inserting two nodes into the chain table, while at the end only one node is actually inserted into the chain table.
Linux from Practice to Ascension No. 24 Signals in Linux If you’re familiar with chained tables, you might see a problem here, a memory leak. And we don’t have any way to find out where node2 is. In the above operation it is possible to re-enter the function before the first call has returned, a phenomenon we call re-entry. A function is not reentrant if it meets one of the following conditions:
  1. Called malloc or free because malloc also manages the heap with a global linked table
  2. Flag I/O library function called Because many implementations of the standard I/O library use global data structures in a non-reentrant manner

5.4 Keyword volatile

volatile is a keyword in C. The purpose of this keyword is to keep memory visible. Some may ask, what is the point of this? You know, it really works. There are many compilers on the market nowadays that perform various optimizations on programs to reduce CPU consumption, but not all optimizations are appropriate, such as the following code:
#include <stdio.h>
#include <signal.h>

int flag = 0;

void handler(int signo)
{
	printf("get a signal:%d\n", signo);
	flag = 1;
}
int main()
{
	signal(2, handler);
	while (!flag);
	printf("Proc Normal Quit!\n");
	return 0;
}
In the code above, we catch signal 2, and when the process receives signal 2, it sets the global variable flag from 0 to 1, which means that until the process receives signal 2, the process will remain in a dead loop, and will not exit until it sets flag to 1 when it receives signal 2. Let’s run it. Linux from Practice to Ascension No. 24 Signals in Linux Here, too, it’s in line with our expectations So this code above must be correct? The answer is no, not after compiler optimization. Because flag is not modified in the main function, if the optimization level is high, the compiler may put the flag variable into a register. The handler execution stream just sets the value of the flag in memory to 1, so even if the process receives signal 2, it won’t jump out of the dead loop. We can experiment. Linux from Practice to Ascension No. 24 Signals in Linux When using an optimizing compiler (e.g., -O3), the compiler performs various optimizations on the code to improve the efficiency of program execution. One common optimization is the elimination of idle waits in loops, i.e., the compiler may detect that while (!flag); this loop does not actually perform any substantial computation or operation, it just waits for flag to become non-zero. The compiler may consider this loop to be an empty loop and optimize it away, thus resulting in not seeing the expected signal processing output. In this case, we just need to add the volatile keyword in front of the flag to avoid this.
volatile int flag = 0;
Linux from Practice to Ascension No. 24 Signals in Linux When we add the volatile keyword, the falg will be visible to memory, so naturally our main execution flow will be able to break out of the dead loop when it changes.

5.5 SIGCHLD signal

In the chapter on processes, we talked about using the wait and waitpid functions to clean up zombie processes. The parent process can either block and wait for its children to finish, or query non-blockingly to see if any of its children have finished and are waiting to be cleaned up (i.e., the polling method).
  • With the first approach, the parent process blocks and can’t handle its own work.
  • With the second approach, the parent process has to remember to poll from time to time while handling its own work, which makes the program implementation complicated.
  • In fact, the child process in the termination of the father process will send SIGCHLD signal, the signal is ignored by default action, the father process can customize the SIGCHLD signal processing function, so that the father process only need to focus on their own work, do not have to care about the child process, the child process will be notified of the termination of the father process, the father process in the signal processing function call wait to clean up the child process can be.
We can write a program to do the following later:
  • The parent process forks the child process, and the child process calls exit() to terminate.
  • Parent process customizes the handler function for the SIGCHLD signal
  • Call wait in it to get the exit status of the child process and print it.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/wait.h>

void handler(int signo)
{
	printf("get a signal: %d\n", signo);
	int ret = 0;
	while ((ret = waitpid(-1, NULL, WNOHANG)) > 0){
		printf("wait child %d success\n", ret);
	}
}
int main()
{
	signal(SIGCHLD, handler);
	if (fork() == 0){
		//child
		printf("child is running, begin dead: %d\n", getpid());
		sleep(3);
		exit(1);
	}
	//father
	while (1);
	return 0;
}
running result Linux from Practice to Ascension No. 24 Signals in Linux The code above catches the SIGCHLD signal and calls the waitpid function in the signal handler to clean up the child process. Caution:
  1. SIGCHLD belongs to the ordinary signal, record the signal of the pending bit only one, if at the same time there are more than one child process at the same time exit, then in the handler function in fact only clean up a child process, so in the use of waitpid function to clean up the child process need to use the while to clean up constantly
  2. When using the waitpid function, you need to set the WNOHANG option, that is, non-blocking wait, otherwise when all the child processes have been cleaned up, due to the while loop, it will call the waitpid function again at this time it will be blocked in this place

5.6 Two signals that cannot be captured and blocked

SIGKILL (9) and SIGSTOP (19)

Recommended Today

[linux] Permission Understanding

catalogs 1. shell commands and how they work 2. The concept of authority 3. Authority management 2.1 Classification of document visitors (persons) 2.2 File types and access rights (thing attributes) 2.3 Representation of file permission values 2.4 Methods for setting file access rights 3. The file directive 4. Permissions for directories★ 5. Sticky bits★ 6. […]