Signal mechanism in Linux kernel-signal sending
Kernel version:2.6.14
CPU architecture:ARM920T
Author:Ce123. (http://blog.csdn.net/ce123)
When an application sends a signal, it mainly uses kill. Note: Do not be confused by "kill". It is not a dedicated function for sending sigkill signals. This function enters the kernel by calling sys_kill (). It receives two parameters:
The first parameter is the target process ID. Kill () can send signals to the process (or process group) and thread (light weight thread). Therefore, PID has the following situations:
- PID> 0: The target process (which may be a lightweight process) is specified by the PID.
- PID = 0: the signal is sent to every process in the current process group.
- PID =-1: The signal is sent to any process, except for the INIT process (pid = 1) and processes that cannot send signals to the current process.
- PID <-1: The signal is sent to the target process group, and its ID is specified by the absolute value of the PID in the parameter.
The second parameter is the signal to be sent.
Because sys_kill processes a lot of data and the analysis is complicated, we start with the tkill () function, which sends the signal to the thread (light weight process) specified by the PID specified by the parameter. The kernel entry of tkill is sys_tkill (kernel/signal. C), which is defined as follows:
/** Send a signal to only one task, even if it's a clone_thread task. */asmlinkage longsys_tkill (int pid, int sig) {struct siginfo Info; int error; struct task_struct * P; /* This is only valid for single tasks */If (PID <= 0) // check the parameter PID return-einval; info. si_signo = SIG; // initialize a siginfo structure info based on the parameter. si_errno = 0; info. si_code = si_tkill; info. si_pid = Current-> tgid; info. si_uid = Current-> uid; read_lock (& TAS Klist_lock); P = find_task_by_pid (PID); // gets the task_struct structure error =-esrch of the thread specified by the PID; If (p) {error = check_kill_permission (SIG, & info, p); // permission check/** the null signal is a permissions and process existence * probe. no signal is actually delivered. */If (! Error & sig & P-> sighand) {spin_lock_irq (& P-> sighand-> siglock); handle_stop_signal (SIG, P); // process certain special signal processes, for example, when receiving sigstop, You need to delete all sigcont in the signal queue. Error = specific_send_sig_info (SIG, & info, P ); // Add the signal to the signal Queue (& P-> sighand-> siglock) ;}} read_unlock (& tasklist_lock); Return Error ;}
The sys_tkill function is implemented through the pecific_send_sig_info () function. Let's take a look at the definition of pecific_send_sig_info () (kernel/signal. C:
Static intspecific_send_sig_info (INT Sig, struct siginfo * info, struct task_struct * t) {int ret = 0; If (! Irqs_disabled () Bug (); assert_spin_locked (& T-> sighand-> siglock); If (unsigned long) info> 2) & (Info-> si_code = si_timer)/** set up a return to indicate that we dropped the signal. */ret = Info-> si_sys_private;/* the signal is ignored * // * Short-Circuit ignored signals. */If (sig_ignored (T, sig) goto out;/* Support queueing exactly one non-RT signal, so that we can get more detailed information about the cause o F the signal. */If (legacy_queue (& T-> pending, sig) goto out; ret = send_signal (SIG, info, T, & T-> pending ); // The actual sending work if (! RET &&! Sigismember (& T-> blocked, sig) signal_wake_up (T, Sig = sigkill); Out: return ret ;}
Call sig_ignored to check whether the signal is ignored, and then check whether the sent signal is a common signal. If it is a common signal, you need to check whether the signal already exists in the current signal Queue Based on the signal bitmap, if it already exists, no processing is required for common signals. Then call send_signal to complete the actual sending work. send_signal () is the focus of signal sending. functions other than sys_tkill are ultimately sent through send_signal.
Note that T-> pending is the link connecting the private signal queue to the parameter passed by send_signal. Finally, if the message is sent successfully, call signal_wake_up () to wake up the target process. This ensures that the process enters the ready state and has the opportunity to be scheduled to execute the signal processing function.
Now let's look at the send_signal () (kernel/signal. c) function. The main task of this function is to allocate and initialize a sigqueue structure, and then add it to the signal queue.
Static int send_signal (INT Sig, struct siginfo * info, struct task_struct * t, struct sigpending * signals) {struct sigqueue * q = NULL; int ret = 0; /** fast-pathed signals for Kernel-Internal things like sigstop * or sigkill. */If (unsigned long) info = 2) goto out_set;/* Real-time signals must be queued if sent by sigqueue, or some other real-time mechanic. it is implementation defined whether kill () Does so. we attempt to do so, on the principle of least surprise, but since kill is not allowed to fail with eagain when low on memory we just make sure at least one signal gets delivered and don't pass on the info struct. */q = _ sigqueue_alloc (T, gfp_atomic, (SIG <sigrtmin & (unsigned long) info <2 | Info-> si_code> = 0 ))); // assign the sigqueue structure if (q) {// if it is successfully allocated to the sigqueue structure, add it to the queue and initialize list_add_t AIl (& Q-> list, & signals-> list); Switch (unsigned long) info) {Case 0: Q-> info. si_signo = SIG; q-> info. si_errno = 0; q-> info. si_code = si_user; q-> info. si_pid = Current-> PID; q-> info. si_uid = Current-> uid; break; Case 1: Q-> info. si_signo = SIG; q-> info. si_errno = 0; q-> info. si_code = si_kernel; q-> info. si_pid = 0; q-> info. si_uid = 0; break; default: copy_siginfo (& Q-> info, Info); // copy the sigqueue structure break;} else {If (SIG> = Sigrtmin & info & (unsigned long) info! = 1 & info-> si_code! = Si_user)/** queue overflow, abort. we may abort if the signal was Rt * and sent by user using something other than kill (). */Return-eagain; If (unsigned long) info> 1) & (Info-> si_code = si_timer )) /** set up a return to indicate that we dropped * the signal. */ret = Info-> si_sys_private;} out_set: sigaddset (& signals-> signal, sig); // set the signal bitmap return ret ;}
From the analysis above, we can see that after the signal is added to the signal queue, signal_wake_up () will be called to wake up the process. The definition of signal_wake_up () (kernel/signal. c) is as follows:
/** Tell a process that it has a new active signal... ** note! We rely on the previous spin_lock to * Lock interrupts for us! We can only be called with * "siglock" held, and the local interrupt must * have been disabled when that got acquired! ** No need to set need_resched since signal event passing * goes through-> blocked */void signal_wake_up (struct task_struct * t, int resume) {unsigned int mask; substring (t, tif_sigpending); // set the tif_sigpending flag for the process/** for sigkill, we want to wake it up in the stopped/traced case. * We don't check t-> state here because there is a race with it * executing another processor and just now enter Ing stopped state. * By Using wake_up_state, we ensure the process will wake up and * handle its death signal. */mask = task_interruptible; If (resume) mask | = task_stopped | task_traced; If (! Wake_up_state (T, mask) kick_process (t );}
Signal_wake_up () first sets the tif_sigpending flag for the process, indicating that the process has a delay signal to wait for processing. Then call wake_up_state () to wake up the target process. If the target process runs on another CPU, wake_up_state () returns 0, and kick_process () is called () sends a processor interrupt to the CPU. When the interrupt returns to the foreplay, it processes the delay signal for the current process.
When the process is scheduled, do_policy_resume () is called to process the signal of the process before the process returns to the user space.