Home  >  Article  >  System Tutorial  >  A brief analysis of linux asynchronous signal handle

A brief analysis of linux asynchronous signal handle

WBOY
WBOYforward
2024-02-13 22:03:13373browse

Linux system is an operating system that supports concurrent execution of multi-tasks. It can run multiple processes at the same time, thereby improving system utilization and efficiency. However, if data exchange and collaboration are required between these processes, some inter-process communication (IPC) methods need to be used, such as message queues, shared memory, semaphores, etc. Among them, signal is a relatively simple and flexible IPC method, which allows one process to send a short message to another process to notify it that some event or exception has occurred. There are two types of signals in Linux systems, namely synchronous signals and asynchronous signals. This article will briefly analyze the Linux asynchronous signal handle method, including the meaning, generation, sending, receiving, processing and ignoring of asynchronous signals.

A brief analysis of linux asynchronous signal handle

When I first learned Linux programming, I always felt that asynchronous signal handle was a very magical thing. User programs can use system calls such as singal to register a signal processing function (handle function) for a certain signal.
The binary code of the program has a certain execution flow in the memory. Why is the program "interrupted" after receiving an asynchronous signal and then jumps to the handle function to run? How can the kernel have the ability to make the program make such a jump? It is impossible to temporarily modify the executable code of the program, right?

Later, after learning some kernel knowledge, I realized that after the process received the signal, it was not "interrupted" immediately. Instead, it first recorded the receipt of a certain signal in the process's control structure (task_struct), and then When the process is about to return from kernel mode to user mode, the process is "interrupted" and the handle function is called.
When will the user process return from kernel mode to user mode? Generally, there are three main situations: system call (the user process actively enters the kernel), interruption (the user process passively enters the kernel), and scheduled execution (the user process changes from waiting for execution to being executed).
It takes a certain amount of time for the process to return from the kernel state to the user state after receiving the signal. But this time is generally very short, at least the clock interrupt will bring the user process into the kernel at a relatively high frequency (for example, once every 1 millisecond) (of course, only for the executing process).

When the process is about to return from the kernel mode to the user mode, if there is a signal that needs to be processed, the corresponding handle function will be called (of course, the handle may not be registered, and the kernel will handle the signal by default). Note that the process is still in kernel mode. How does the kernel call the handle function in user mode?
Can I call it directly? Of course not. The kernel code runs under a high CPU privilege level. If the handle function is called directly, the handle function will also be executed under the same CPU privilege. Then the user will be able to do whatever he wants in the handle function.
Therefore, calling handle must first return to user mode. But after returning to user mode, the program flow is no longer controlled by the kernel. Is it possible that the kernel really temporarily changes the executable code of the user process?

The actual approach of the kernel is quite clever. After a user process enters the kernel, it will leave a return address on its corresponding kernel stack so that the process can return. The way the kernel calls the handle function is to temporarily change the return address on the stack, and then return according to the original process of returning to user mode. As a result, this return goes to the handle function. (Of course, it is not just the return address that needs to be modified, but the entire call stack.)
Although the return address has been temporarily changed, the user process will eventually return to the original return address. So, where should the original return address and its call stack be saved? The kernel stack space of the process is limited, and it also needs to deal with system calls that may occur in the handle function, so it is unrealistic for the kernel to put this information on the kernel stack, and it can only be pushed onto the user stack.

When the handle function is executed, the execution process returns to the kernel. Similarly, due to different CPU privilege levels, you cannot simply use the RET instruction to return from the handle function to the kernel. A system call needs to be executed.

After the handle is executed, why do we need to return to the kernel and then return from the kernel to the original return address? It would be very convenient if you directly return to the original return address. And it is not difficult to do this. The original return address and its call stack have been pushed onto the user stack. The kernel only needs to do a little manipulation on the call stack of the handle function.
1. Returning to the original return address does not just mean returning to that address. You need to restore the entire scene (mainly registers and so on). Of course, the kernel can also press some code on the user stack to complete these things;
2. Now there may be more than one signal to process. It is best to let the user process return to the kernel and continue processing other signals;

In order to return to the kernel, first, the kernel pushes a return address onto the user stack before returning to the handle function, so that it can return to the specified address when returning from the handle. This specified address is actually also on the user stack of the process. The kernel places several instructions on this address (places executable code on the stack) to let the process call a system call called sigreturn.

The user stack before returning to the handle function is roughly as follows:
Original data -> Instruction to call sigreturn (let its address be a) -> Original return address and its call stack -> Return address (value is a) -> Handle's stack variable

The kernel places the sigreturn instruction on the call stack of the handle function, which is the practice in Linux 2.4. Every time the user's handle function is called, so many instructions need to be copied to the user stack, which is not good.
Linux 2.6 has a page called vsyscall page, which contains some instructions prepared by the kernel for user programs, including calling the sigreturn instruction. This vsyscall page is mapped to the end of the virtual address space of each process, is shared by all user processes, and is read-only for user processes. In this way, there is no need to insert the sigreturn instruction into the call stack of the handle function. Simply set the return address of the handle function to the corresponding code in the vsyscall page.

In order to automatically call sigreturn to return to the kernel after the handle is executed, the kernel does a lot of things. So can we agree to let users call sigreturn themselves?
Of course, this is possible. Just to make the signal processing mechanism a complete mechanism, the kernel did not do this. Otherwise, if the user forgets to call sigreturn in the handle function, the process may crash inexplicably. And it is difficult for the compiler to find such errors.

After the process calls the sigreturn system call and re-enters the kernel, the original return address and its call stack pressed on the user stack are obtained. Eventually, the kernel will modify the stack so that the process returns to the original return address when it returns to user space.

This article briefly analyzes the Linux asynchronous signal handle method, including the meaning, generation, sending, receiving, processing and ignoring of asynchronous signals. By understanding and mastering this knowledge, we can master the core knowledge of Linux signal processing, thereby improving the stability and efficiency of the system. Of course, Linux asynchronous signal handle has many other features and usages, which require continuous learning and research. I hope this article can bring you some inspiration and help.

The above is the detailed content of A brief analysis of linux asynchronous signal handle. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:lxlinux.net. If there is any infringement, please contact admin@php.cn delete