Linux asynchronous signal handle Analysis

Source: Internet
Author: User

When I was a beginner in Linux programming, I always thought that asynchronous signal handle is a magical thing.ProgramYou can use a system call such as Singal to register a handle function for a certain signal ).
Binary ProgramCodeThere is a definite Execution Process in the memory. Why will the program be "interrupted" after receiving the asynchronous signal and then jump to the handle function to run it? How can the kernel enable the program to perform such a jump? It is impossible to temporarily modify the executable code of the program?

Later I learned some kernel knowledge,Only to knowAfter receiving the signal, the process was not immediately "interrupted", but first recorded and received a certain signal in the process control structure (task_struct, then, when the process is about to return the user State from the kernel state, the process is "interrupted" and the handle function is called.

When will a user process return the user State from the kernel state? Generally, there are three main situations: system calls (user processes actively enter the kernel), interruptions (user processes passively enter the kernel), and scheduled execution (user processes change from waiting for execution to being executed ).
It takes some time for a process to return a user State from the received signal to the kernel state. However, this time is usually very short, and at least the clock interruption will bring the user process into the kernel at a large frequency (for example, once every 1 ms) (of course, only for the process being executed ).

When a process is about to return the user State from the kernel state, if there is a signal to be processed, the corresponding handle function will be called (of course, handle may not be registered, at this time, the Kernel performs default processing on the signal ). Note: The process is still in the kernel state. How does the kernel call the handle function in the user State?
Can I call it directly? Of course not. The kernel code runs at the high CPU privilege level. If you call the handle function directly, the handle function will also be executed under the same CPU privilege. You can do whatever you want in the handle function.Therefore, handle must first return the user State. But after returning to the user State, the program process is not controlled by the kernel. Is it hard for the kernel to temporarily change the executable code of the user process?

The actual kernel practices are clever. After a user process enters the kernel, the return address will be left on the corresponding Kernel stack for the process to return. The kernel calls the handle function to temporarily remove the return address on the stack, and then return the address based on the original process of returning the user State. The result returns to the handle function. (Of course, you need to modify not only the return address, but the entire call stack .)
Although the return address has been changed temporarily, the user process will eventually return to the original return address. So where should the original return address and its call stack be stored? The kernel stack space of a process is limited, and it also needs to cope with system calls that may occur in the handle function. Therefore, it is unrealistic for the kernel to put the information on the kernel stack, it can only be pushed to the user stack.

After the handle function is executed, the execution process is returned to the kernel. Similarly, because of the different CPU privilege levels, the returned kernel from the handle function cannot be simply returned using the RET command. A system call is required.

After handle is executed, why should we return to the kernel and then return to the original return address from the kernel? It is convenient to directly return to the original return address. It is not difficult to do this. The original return address and its call stack have been pressed to the user stack. The kernel only needs to do a little bit on the call stack of the handle function.
1. returning to the original return address does not mean returning to that address. You need to restore the entire site (mainly registers or something ). Of course, the kernel can also press some code on the user stack to complete these tasks;
2. There may be more than one signal to process now. It is best to let the user process return to the kernel and continue to process other signals;

To return to the kernel, the kernel first presses a return address to the user stack before returning it to the handle function,In this way, the handle can return to the specified address. The specified address is also on the user stack of the process, and the kernel places several commands on the address (executable code on the stack ), let the process call a system call called sigreturn.

The user stack before returning to the handle function is roughly as follows:
Original data-> call the sigreturn command (set its address to a)-> original return address and its call stack-> return address (value: a)-> handle stack variable

The kernel places the sigreturn command on the call stack of the handle function. This is what we did in Linux 2.4. Each time you call a user's handle function, you need to copy these commands to the user stack, which is not very good.
Linux 2.6 has a page called vsyscall page, which contains commands prepared by the kernel for the user program, including calling the sigreturn command. This vsyscall page is mapped to the virtual address space near the end of each process, shared by all user processes, and read-only for user processes. In this way, the handle function does not need to be inserted into the sigreturn command on the call stack. You can directly set the return address of the handle function to the corresponding code on the vsyscall page.

To enable the handle to automatically call sigreturn to return to the kernel after execution, the kernel has done many things. So can we make an appointment to allow users to call sigreturn on their own?
Of course, this is acceptable. It is only to make the signal processing mechanism a complete mechanism, and the kernel does not. Otherwise, if you forget to call sigreturn in the handle function, the process may crash inexplicably. It is difficult for the compiler to identify such errors.

After the process calls the sigreturn System Call and re-enters the kernel, the original return address on the user stack and its call Stack are obtained. In the end, the kernel modifies the stack so that the process returns the original return address when returning the user space.

When I was a beginner in Linux programming, I always thought that asynchronous signal handle is a magical thing, user Programs can use system calls such as Singal to register a handle function for a certain signal ).
The binary code of the program has a definite Execution Process in the memory. Why is the program interrupted after receiving the asynchronous signal and then jumps to the handle function to run it? How can the kernel enable the program to perform such a jump? It is impossible to temporarily modify the executable code of the program?

Later I learned some kernel knowledge,Only to knowAfter receiving the signal, the process was not immediately "interrupted", but first recorded and received a certain signal in the process control structure (task_struct, then, when the process is about to return the user State from the kernel state, the process is "interrupted" and the handle function is called.

When will a user process return the user State from the kernel state? Generally, there are three main situations: system calls (user processes actively enter the kernel), interruptions (user processes passively enter the kernel), and scheduled execution (user processes change from waiting for execution to being executed ).
It takes some time for a process to return a user State from the received signal to the kernel state. However, this time is usually very short, and at least the clock interruption will bring the user process into the kernel at a large frequency (for example, once every 1 ms) (of course, only for the process being executed ).

When a process is about to return the user State from the kernel state, if there is a signal to be processed, the corresponding handle function will be called (of course, handle may not be registered, at this time, the Kernel performs default processing on the signal ). Note: The process is still in the kernel state. How does the kernel call the handle function in the user State?
Can I call it directly? Of course not. The kernel code runs at the high CPU privilege level. If you call the handle function directly, the handle function will also be executed under the same CPU privilege. You can do whatever you want in the handle function.Therefore, handle must first return the user State. But after returning to the user State, the program process is not controlled by the kernel. Is it hard for the kernel to temporarily change the executable code of the user process?

The actual kernel practices are clever. After a user process enters the kernel, the return address will be left on the corresponding Kernel stack for the process to return. The kernel calls the handle function to temporarily remove the return address on the stack, and then return the address based on the original process of returning the user State. The result returns to the handle function. (Of course, you need to modify not only the return address, but the entire call stack .)
Although the return address has been changed temporarily, the user process will eventually return to the original return address. So where should the original return address and its call stack be stored? The kernel stack space of a process is limited, and it also needs to cope with system calls that may occur in the handle function. Therefore, it is unrealistic for the kernel to put the information on the kernel stack, it can only be pushed to the user stack.

After the handle function is executed, the execution process is returned to the kernel. Similarly, because of the different CPU privilege levels, the returned kernel from the handle function cannot be simply returned using the RET command. A system call is required.

After handle is executed, why should we return to the kernel and then return to the original return address from the kernel? It is convenient to directly return to the original return address. It is not difficult to do this. The original return address and its call stack have been pressed to the user stack. The kernel only needs to do a little bit on the call stack of the handle function.
1. returning to the original return address does not mean returning to that address. You need to restore the entire site (mainly registers or something ). Of course, the kernel can also press some code on the user stack to complete these tasks;
2. There may be more than one signal to process now. It is best to let the user process return to the kernel and continue to process other signals;

To return to the kernel, the kernel first presses a return address to the user stack before returning it to the handle function,In this way, the handle can return to the specified address. The specified address is also on the user stack of the process, and the kernel places several commands on the address (executable code on the stack ), let the process call a system call called sigreturn.

The user stack before returning to the handle function is roughly as follows:
Original data-> call the sigreturn command (set its address to a)-> original return address and its call stack-> return address (value: a)-> handle stack variable

The kernel places the sigreturn command on the call stack of the handle function. This is what we did in Linux 2.4. Each time you call a user's handle function, you need to copy these commands to the user stack, which is not very good.
Linux 2.6 has a page called vsyscall page, which contains commands prepared by the kernel for the user program, including calling the sigreturn command. This vsyscall page is mapped to the virtual address space near the end of each process, shared by all user processes, and read-only for user processes. In this way, the handle function does not need to be inserted into the sigreturn command on the call stack. You can directly set the return address of the handle function to the corresponding code on the vsyscall page.

To enable the handle to automatically call sigreturn to return to the kernel after execution, the kernel has done many things. So can we make an appointment to allow users to call sigreturn on their own?
Of course, this is acceptable. It is only to make the signal processing mechanism a complete mechanism, and the kernel does not. Otherwise, if you forget to call sigreturn in the handle function, the process may crash inexplicably. It is difficult for the compiler to identify such errors.

After the process calls the sigreturn System Call and re-enters the kernel, the original return address on the user stack and its call Stack are obtained. In the end, the kernel modifies the stack so that the process returns the original return address when returning the user space.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.