Obtain the received signal in the kernel thread

Source: Internet
Author: User

When testing and developing the kernel module, we found a BUG: If the module is not uninstalled and the reboot command is used to restart the system, the system cannot be restarted, check the log and find that the created kernel thread is in an endless loop, causing the system to fail to restart. Check the code and find that the cause of the problem is that when the system call returns-EINTR (that is, the signal is interrupted), the loop in the kernel thread does not exit, but continues the loop operation, this logic is consistent with the business and there is no error. The problem is that the system does not check what signal is received. If the signal is sent when the system is restarted or the signal is sent when the system is shut down, the loop should be exited. The rest is to find the method to obtain the received signal in the kernel thread. Obtain the blocked signal in the user State and call sigpending (). Therefore, first try to call sys_sigpending () to obtain the blocked signal. Sys_sigpending () is not exported as a system call, so it cannot be called directly. However, you can use the/proc/kallsyms file to obtain the address of sys_sigpending () to call this function. On my test machine, the sys_sigpending () address is 0xffffffff810802e0. The test code is as follows: [cpp] view plaincopy/** fcluster. c */# include <linux/init. h> # include <linux/module. h> # include <linux/signal. h> # include <linux/spinlock. h> # include <linux/sched. h> # include <linux/uaccess. h> static int remove_mod = 0; static int my_sigpending (sigset_t * set) {int (* sigpending) (sigset_t * set); int ret; mm_segment_t old_fs; sigpending = (typeof (sigpending) 0xffffffff810802e0; old_fs = g Et_fs (); set_fs (get_ds (); ret = sigpending (set); set_fs (old_fs); return ret;} static int thread_process (void * arg) {sigset_t * sigset, _ sigset; sigset = & __ sigset; allow_signal (SIGURG); allow_signal (SIGTERM); allow_signal (SIGKILL); allow_signal (SIGSTOP); allow_signal (SIGCONT ); printk (KERN_ALERT "the pid of thread_process is % d. \ n ", current-> pid); my_sigpending (sigset); printk (KERN_ALERT" Before Receive signal, signal map: 0x % lX. \ n ", sigset-> sig [0]); (;! Remove_mod;) {/* Avoid infinite loop */msleep (1000); if (signal_pending (current) {my_sigpending (sigset); printk (KERN_ALERT "Received signal, signal map: 0x % lX. \ n ", sigset-> sig [0]); printk (KERN_ALERT" Receive SIGURG signal? % S. \ n ", sigismember (sigset, SIGURG )? "True": "false"); printk (KERN_ALERT "Receive SIGTERM signal? % S. \ n ", sigismember (sigset, SIGTERM )? "True": "false"); printk (KERN_ALERT "Receive SIGKILL signal? % S. \ n ", sigismember (sigset, SIGKILL )? "True": "false"); printk (KERN_ALERT "Receive SIGSTOP signal? % S. \ n ", sigismember (sigset, SIGSTOP )? "True": "false");/* Use halt to stop the system */printk (KERN_ALERT "Receive SIGCONT signal? % S. \ n ", sigismember (sigset, SIGCONT )? "True": "false"); break;} return 0;} static int _ init fcluster_init (void) {kernel_thread (thread_process, NULL, CLONE_FILES); return 0 ;} static void _ exit fcluster_exit (void) {remove_mod = 1; msleep (2000);} MODULE_LICENSE ("GPL"); module_init (fcluster_init); module_exit (fcluster_exit ); if the kernel thread wants to receive signals sent by the user terminal, it must call allow_signal () in the processing function to specify which signals can be received. My_sigpending () is a simple encapsulation of sys_sigpending (), used to obtain the signal of the current kernel thread blocking. Compile the above code into a kernel module, insert it into the system, open the system log, and view the ID of the created kernel thread (my name is 3278, as shown in ), then, use the kill command on another terminal to send the SIGTERM command to the created kernel thread. The test result should be viewed through the system log file (/var/log/messages), as shown in: view the system log and find that the obtained signal bitmap is 0! It is impossible, because the above Code shows that only when the signal_pending () function returns true (that is, when the signal is received) Can the log information be output. The code is very simple. The key function is my_sigpending (), which is a simple encapsulation of sys_sigpending (). Therefore, we still need to find the cause from the implementation of sys_sigpending. View the source code of sys_sigpending (), which is a simple encapsulation of the do_sigpending () function. Continue to find the cause from do_sigpending. Do_sigpending () source code: [cpp] view plaincopylong do_sigpending (void _ user * set, unsigned long sigsetsize) {long error =-EINVAL; sigset_t pending; if (sigsetsize> sizeof (sigset_t) goto out; spin_lock_irq (& current-> sighand-> siglock); sigorsets (& pending, & current-> pending. signal, & current-> signal-> shared_pending.signal); spin_unlock_irq (& current-> sighand-> siglock);/* Outside the lock because only t His thread touches it. */sigandsets (& pending, & current-> blocked, & pending); error =-EFAULT; if (! Copy_to_user (set, & pending, sigsetsize) error = 0; out: return error;} do_sigpending () first calls sigorsets () to set the current process signal information (current-> pending. signal and current-> signal-> shared_pending.signal) perform or operate (that is, merge the signal masks of the two locations) and store them in the Temporary Variable pending. After obtaining the signal mask of the current process, you must call sigandsets () to transfer the pending and the signal mask of the current process to the upper layer. The problem is to determine the pending value and the current-> blocked value before calling sigandsets. Modify the thread_process () function based on do_sigpending () to print the signal bitmap and signal mask of the current process. The modified thread_process () function is as follows: [cpp] view plaincopystatic int thread_process (void * arg) {sigset_t * sigset, _ sigset; sigset = & __ sigset; allow_signal (SIGURG); allow_signal (SIGTERM ); allow_signal (SIGKILL); allow_signal (SIGSTOP); allow_signal (SIGCONT); printk (KERN_ALERT "the pid of thread_process is % d. \ n ", current-> pid); spin_lock_irq (& current-> Sighand-> siglock); sigorsets (sigset, & current-> pending. signal, & current-> signal-> shared_pending.signal); spin_unlock_irq (& current-> sighand-> siglock <span style = "font-family: Arial, Helvetica, sans-serif; ">); </span> printk (KERN_ALERT" Before receive signal, signal map: 0x % lX. \ n ", sigset-> sig [0]); printk (KERN_ALERT" Beofore receive signal, blocked map: 0x % lX. \ n ", current-> blocked. sig [0]); (;! Remove_mod;) {/* Avoid infinite loop */msleep (1000); if (signal_pending (current) {spin_lock_irq (& current-> sighand-> siglock); sigorsets (sigset, & current-> pending. signal, & current-> signal-> shared_pending.signal); spin_unlock_irq (& current-> sighand-> siglock); printk (KERN_ALERT "Received signal, signal map: 0x % lX. \ n ", sigset-> sig [0]); printk (KERN_ALERT" Receive SIGURG signal? % S. \ n ", sigismember (sigset, SIGURG )? "True": "false"); printk (KERN_ALERT "Receive SIGTERM signal? % S. \ n ", sigismember (sigset, SIGTERM )? "True": "false"); printk (KERN_ALERT "Receive SIGKILL signal? % S. \ n ", sigismember (sigset, SIGKILL )? "True": "false"); printk (KERN_ALERT "Receive SIGSTOP signal? % S. \ n ", sigismember (sigset, SIGSTOP )? "True": "false");/* Use halt to stop the system */printk (KERN_ALERT "Receive SIGCONT signal? % S. \ n ", sigismember (sigset, SIGCONT )? "True": "false"); break;} return 0;} the test result is as follows: the blue part shows that current-> blocked is 0, that is to say, the signal mask of the current kernel thread is 0. Therefore, in do_sigpending (), the result of calling sigandsets () to execute and operate the signal bitmap and mask of the current process is always 0. From the test results, it is not feasible to call sys_sigpending () to obtain the kernel thread method. To obtain the signal received by the kernel thread, you can call current-> pending. signals in signal and current> signal> shared_pending.signal are merged. So far, we have solved the first problem: how to obtain the signal received by the kernel thread. The next solution is to obtain the signal received by the kernel thread when the system is restarted. After reboot, the system restarts. After restart, the information output by the module is not stored in the system log/var/log/messages. You need to find another way to obtain the log information output by the module during restart. Google had nothing to gain, and finally thought of using kdump + crash to get the system log information during the restart. Kdump can be used to dump the generated core file when the kernel crashes. crash is used to analyze the generated core file. The dmesg command in crash can be used to view the log information during system crash. So if we receive a signal in our kernel thread and print the log information, let the kernel crash and we can see the output log information. It is easy to crash the kernel. You can use the BUG () Macro or directly call the panic () function. Modify the thread_process () function and replace the "break;" statement in the if branch with BUG () or panic (). The Code does not need to be pasted. directly upload the test result: in the blue circle, we can see that the SIGTERM and SIGCONT signals are received at the same time. However, the test result is tested by a virtual machine at home, and the actual server (Blade Machine) is used in the company) only the SIGTERM signal is detected during the test. I wanted to make the test results look the same as the actual server, but I want to give a reminder to anyone who has not been cheated by the virtual machine through this details, when testing and developing programs, you must use the same environment as production. Try not to use virtual machines for testing. When I tested a network-related module, I spent a whole morning with my colleagues and finally confirmed that it was a problem with the virtual NIC Driver provided by the virtual machine! In addition to the signal received by the kernel thread during system restart, the signal received by the kernel thread during shutdown is also tested. It is found that the signal received by the kernel thread during shutdown is actually SIGCONT, instead of SIGKILL or SIGSTOP! If you encounter any uncertain problems in the future, you must test them by yourself to avoid errors. Some may ask, why don't we directly look at sys_reboot () system calls to see what signals will be sent to the kernel thread? So much trouble to test? Before the test, I read the source code of sys_reboot (), but I did not find the signal to be sent for a long time, and finally gave up, it seems that there is no need to spend too much time studying the kernel processing when the system is restarted or shut down. The kernel is too huge now. If you have studied all aspects of the kernel, it is unrealistic and meaningless. For the time being, we will focus on what we are currently using and wait for time to study other areas of interest.

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.