應(yīng)用程序發(fā)送信號時,主要通過kill進行。注意:不要被“kill”迷惑,它并不是發(fā)送SIGKILL信號專用函數(shù)。這個函數(shù)主要通過系統(tǒng)調(diào)用sys_kill()進入內(nèi)核,它接收兩個參數(shù):
第一個參數(shù)為目標(biāo)進程id,kill()可以向進程(或進程組),線程(輕權(quán)線程)發(fā)送信號,因此pid有以下幾種情況:
● pid>0:目標(biāo)進程(可能是輕權(quán)進程)由pid指定。
● pid=0:信號被發(fā)送到當(dāng)前進程組中的每一個進程。
● pid=-1:信號被發(fā)送到任何一個進程,init進程(PID=1)和以及當(dāng)前進程無法發(fā)送信號的進程除外。
● pid<-1:信號被發(fā)送到目標(biāo)進程組,其id由參數(shù)中的pid的絕對值指定。
第二個參數(shù)為需要發(fā)送的信號。
由于sys_kill處理的情況比較多,分析起來比較復(fù)雜,我們從太累了入手,這個函數(shù)把信號發(fā)送到由參數(shù)指定pid指定的線程(輕權(quá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進行檢查
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);
//對某些特殊信號進程處理,例如當(dāng)收到SIGSTOP時,需要把信號隊列中的SIGCONT全部刪除
error = specific_send_sig_info(sig, &info, p);//把信號加入到信號隊列
spin_unlock_irq(&p->sighand->siglock);
}
}
read_unlock(&tasklist_lock);
return error;
}
sys_tkill函數(shù)主要是通過pecific_send_sig_info()函數(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);//實際的發(fā)送工作
if (!ret && !sigismember(&t->blocked, sig))
signal_wake_up(t, sig == SIGKILL);
out:
return ret;
}
首先調(diào)用sig_ignored檢查信號是否被忽略,然后檢查發(fā)送的信號是不是普通信號,如果是普通信號,就需要根據(jù)信號位圖來檢查當(dāng)前信號隊列中是否已經(jīng)存在該信號,如果已經(jīng)存在,對于普通信號不需要做任何處理。然后調(diào)用send_signal來完成實際的發(fā)送工作,send_signal()是信號發(fā)送的重點,除sys_tkill之外的函數(shù),最終都是通過send_signal()來完成信號的發(fā)送工作的。
這里注意到想send_signal()傳遞的參數(shù)時t->pending,也就是連接Private Signal Queue的那條鏈。最后,如果發(fā)送成功就調(diào)用signal_wake_up()來喚醒目標(biāo)進程,這樣可以保證該進程進入就緒狀態(tài),從而有機會被調(diào)度執(zhí)行信號處理函數(shù)。
現(xiàn)在我們來看看send_signal()(kernel/signal.c)函數(shù),這個函數(shù)的主要工作就是分配并初始化一個sigqueue結(jié)構(gòu),然后把它添加到信號隊列中。
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),就把它添加到隊列中,并對其初始化
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;
}
從上面的分析可以看出,我們看到信號被添加到信號隊列之后,會調(diào)用signal_wake_up()喚醒這個進程,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);//為進程設(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);
}