linux內(nèi)核中的信號機(jī)制--信號發(fā)送
Kernel version:2.6.14 CPU architecture:ARM920T Author:ce123(http://blog.csdn.net/ce123)
應(yīng)用程序發(fā)送信號時,,主要通過kill進(jìn)行,。注意:不要被“kill”迷惑,,它并不是發(fā)送SIGKILL信號專用函數(shù),。這個函數(shù)主要通過系統(tǒng)調(diào)用sys_kill()進(jìn)入內(nèi)核,,它接收兩個參數(shù): 第一個參數(shù)為目標(biāo)進(jìn)程id,,kill()可以向進(jìn)程(或進(jìn)程組),線程(輕權(quán)線程)發(fā)送信號,,因此pid有以下幾種情況: - pid>0:目標(biāo)進(jìn)程(可能是輕權(quán)進(jìn)程)由pid指定,。
- pid=0:信號被發(fā)送到當(dāng)前進(jìn)程組中的每一個進(jìn)程。
- pid=-1:信號被發(fā)送到任何一個進(jìn)程,,init進(jìn)程(PID=1)和以及當(dāng)前進(jìn)程無法發(fā)送信號的進(jìn)程除外,。
- pid<-1:信號被發(fā)送到目標(biāo)進(jìn)程組,其id由參數(shù)中的pid的絕對值指定,。
第二個參數(shù)為需要發(fā)送的信號,。 由于sys_kill處理的情況比較多,分析起來比較復(fù)雜,,我們從tkill()函數(shù)入手,,這個函數(shù)把信號發(fā)送到由參數(shù)指定pid指定的線程(輕權(quán)進(jìn)程)中。tkill的內(nèi)核入口是sys_tkill(kernel/signal.c),,其定義如下: - /*
- * Send a signal to only one task, even if it's a CLONE_THREAD task.
- */
- asmlinkage long
- sys_tkill(int pid, int sig)
- {
- struct siginfo info;
- int error;
- struct task_struct *p;
-
- /* This is only valid for single tasks */
- if (pid <= 0)//對參數(shù)pid進(jìn)行檢查
- return -EINVAL;
-
- info.si_signo = sig; //根據(jù)參數(shù)初始化一個siginfo結(jié)構(gòu)
- info.si_errno = 0;
- info.si_code = SI_TKILL;
- info.si_pid = current->tgid;
- info.si_uid = current->uid;
-
- read_lock(&tasklist_lock);
- p = find_task_by_pid(pid);//獲取由pid指定的線程的task_struct結(jié)構(gòu)
- error = -ESRCH;
- if (p) {
- error = check_kill_permission(sig, &info, p);//權(quán)限檢查
- /*
- * 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);
- //對某些特殊信號進(jìn)程處理,,例如當(dāng)收到SIGSTOP時,需要把信號隊(duì)列中的SIGCONT全部刪除
- error = specific_send_sig_info(sig, &info, p);//把信號加入到信號隊(duì)列
- spin_unlock_irq(&p->sighand->siglock);
- }
- }
- read_unlock(&tasklist_lock);
- return error;
- }
sys_tkill函數(shù)主要是通過pecific_send_sig_info()函數(shù)實(shí)現(xiàn)的,下面我們看一下pecific_send_sig_info()(kernel/signal.c)的定義: - static int
- specific_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;
- /*信號被忽略*/
- /* 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 of
- the signal. */
- if (LEGACY_QUEUE(&t->pending, sig))
- goto out;
-
- ret = send_signal(sig, info, t, &t->pending);//實(shí)際的發(fā)送工作
- if (!ret && !sigismember(&t->blocked, sig))
- signal_wake_up(t, sig == SIGKILL);
- out:
- return ret;
- }
首先調(diào)用sig_ignored檢查信號是否被忽略,,然后檢查發(fā)送的信號是不是普通信號,,如果是普通信號,就需要根據(jù)信號位圖來檢查當(dāng)前信號隊(duì)列中是否已經(jīng)存在該信號,,如果已經(jīng)存在,,對于普通信號不需要做任何處理。然后調(diào)用send_signal來完成實(shí)際的發(fā)送工作,,send_signal()是信號發(fā)送的重點(diǎn),,除sys_tkill之外的函數(shù),最終都是通過send_signal()來完成信號的發(fā)送工作的,。 這里注意到想send_signal()傳遞的參數(shù)時t->pending,,也就是連接Private Signal Queue的那條鏈。最后,,如果發(fā)送成功就調(diào)用signal_wake_up()來喚醒目標(biāo)進(jìn)程,,這樣可以保證該進(jìn)程進(jìn)入就緒狀態(tài),從而有機(jī)會被調(diào)度執(zhí)行信號處理函數(shù),。 現(xiàn)在我們來看看send_signal()(kernel/signal.c)函數(shù),,這個函數(shù)的主要工作就是分配并初始化一個sigqueue結(jié)構(gòu),然后把它添加到信號隊(duì)列中,。 - 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 mechanism. 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)));//分配sigqueue結(jié)構(gòu)
- if (q) {//如果成功分配到sigqueue結(jié)構(gòu),,就把它添加到隊(duì)列中,并對其初始化
- list_add_tail(&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);//拷貝sigqueue結(jié)構(gòu)
- 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);//設(shè)置信號位圖
- return ret;
- }
從上面的分析可以看出,,我們看到信號被添加到信號隊(duì)列之后,,會調(diào)用signal_wake_up()喚醒這個進(jìn)程,signal_wake_up()(kernel/signal.c)的定義如下:
- /*
- * 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;
-
- set_tsk_thread_flag(t, TIF_SIGPENDING);//為進(jìn)程設(shè)置TIF_SIGPENDING標(biāo)志
-
- /*
- * 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 entering 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()首先為進(jìn)程設(shè)置TIF_SIGPENDING標(biāo)志,,說明該進(jìn)程有延遲的信號要等待處理,。然后再調(diào)用wake_up_state()喚醒目標(biāo)進(jìn)程,如果目標(biāo)進(jìn)程在其他的CPU上運(yùn)行,,wake_up_state()將返回0,,此時調(diào)用kick_process()向該CPU發(fā)送一個處理器間中斷。當(dāng)中斷返回前戲,,會為當(dāng)前進(jìn)程處理延遲的信號,。 此后當(dāng)該進(jìn)程被調(diào)度時,在進(jìn)程返回用戶空間前,,會調(diào)用do_notify_resume()處理該進(jìn)程的信號,。
|