linux內(nèi)核中的信號(hào)機(jī)制--信號(hào)發(fā)送
CPU architecture:ARM920T
本文引用地址:http://m.butianyuan.cn/article/201611/320005.htmAuthor:ce123(http://blog.csdn.net/ce123)
應(yīng)用程序發(fā)送信號(hào)時(shí),主要通過(guò)kill進(jìn)行。注意:不要被“kill”迷惑,它并不是發(fā)送SIGKILL信號(hào)專用函數(shù)。這個(gè)函數(shù)主要通過(guò)系統(tǒng)調(diào)用sys_kill()進(jìn)入內(nèi)核,它接收兩個(gè)參數(shù):
第一個(gè)參數(shù)為目標(biāo)進(jìn)程id,kill()可以向進(jìn)程(或進(jìn)程組),線程(輕權(quán)線程)發(fā)送信號(hào),因此pid有以下幾種情況:
- pid>0:目標(biāo)進(jìn)程(可能是輕權(quán)進(jìn)程)由pid指定。
- pid=0:信號(hào)被發(fā)送到當(dāng)前進(jìn)程組中的每一個(gè)進(jìn)程。
- pid=-1:信號(hào)被發(fā)送到任何一個(gè)進(jìn)程,init進(jìn)程(PID=1)和以及當(dāng)前進(jìn)程無(wú)法發(fā)送信號(hào)的進(jìn)程除外。
- pid<-1:信號(hào)被發(fā)送到目標(biāo)進(jìn)程組,其id由參數(shù)中的pid的絕對(duì)值指定。
由于sys_kill處理的情況比較多,分析起來(lái)比較復(fù)雜,我們從tkill()函數(shù)入手,這個(gè)函數(shù)把信號(hào)發(fā)送到由參數(shù)指定pid指定的線程(輕權(quán)進(jìn)程)中。tkill的內(nèi)核入口是sys_tkill(kernel/signal.c),其定義如下:
- /*
- *Sendasignaltoonlyonetask,evenifitsaCLONE_THREADtask.
- */
- asmlinkagelong
- sys_tkill(intpid,intsig)
- {
- structsiginfoinfo;
- interror;
- structtask_struct*p;
- /*Thisisonlyvalidforsingletasks*/
- if(pid<=0)//對(duì)參數(shù)pid進(jìn)行檢查
- return-EINVAL;
- info.si_signo=sig;//根據(jù)參數(shù)初始化一個(gè)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)限檢查
- /*
- *Thenullsignalisapermissionsandprocessexistence
- *probe.Nosignalisactuallydelivered.
- */
- if(!error&&sig&&p->sighand){
- spin_lock_irq(&p->sighand->siglock);
- handle_stop_signal(sig,p);
- //對(duì)某些特殊信號(hào)進(jìn)程處理,例如當(dāng)收到SIGSTOP時(shí),需要把信號(hào)隊(duì)列中的SIGCONT全部刪除
- error=specific_send_sig_info(sig,&info,p);//把信號(hào)加入到信號(hào)隊(duì)列
- spin_unlock_irq(&p->sighand->siglock);
- }
- }
- read_unlock(&tasklist_lock);
- returnerror;
- }
sys_tkill函數(shù)主要是通過(guò)pecific_send_sig_info()函數(shù)實(shí)現(xiàn)的,下面我們看一下pecific_send_sig_info()(kernel/signal.c)的定義:
- staticint
- specific_send_sig_info(intsig,structsiginfo*info,structtask_struct*t)
- {
- intret=0;
- if(!irqs_disabled())
- BUG();
- assert_spin_locked(&t->sighand->siglock);
- if(((unsignedlong)info>2)&&(info->si_code==SI_TIMER))
- /*
- *Setupareturntoindicatethatwedroppedthesignal.
- */
- ret=info->si_sys_private;
- /*信號(hào)被忽略*/
- /*Short-circuitignoredsignals.*/
- if(sig_ignored(t,sig))
- gotoout;
- /*Supportqueueingexactlyonenon-rtsignal,sothatwe
- cangetmoredetailedinformationaboutthecauseof
- thesignal.*/
- if(LEGACY_QUEUE(&t->pending,sig))
- gotoout;
- ret=send_signal(sig,info,t,&t->pending);//實(shí)際的發(fā)送工作
- if(!ret&&!sigismember(&t->blocked,sig))
- signal_wake_up(t,sig==SIGKILL);
- out:
- returnret;
- }
這里注意到想send_signal()傳遞的參數(shù)時(shí)t->pending,也就是連接Private Signal Queue的那條鏈。最后,如果發(fā)送成功就調(diào)用signal_wake_up()來(lái)喚醒目標(biāo)進(jìn)程,這樣可以保證該進(jìn)程進(jìn)入就緒狀態(tài),從而有機(jī)會(huì)被調(diào)度執(zhí)行信號(hào)處理函數(shù)。
現(xiàn)在我們來(lái)看看send_signal()(kernel/signal.c)函數(shù),這個(gè)函數(shù)的主要工作就是分配并初始化一個(gè)sigqueue結(jié)構(gòu),然后把它添加到信號(hào)隊(duì)列中。
- staticintsend_signal(intsig,structsiginfo*info,structtask_struct*t,
- structsigpending*signals)
- {
- structsigqueue*q=NULL;
- intret=0;
- /*
- *fast-pathedsignalsforkernel-internalthingslikeSIGSTOP
- *orSIGKILL.
- */
- if((unsignedlong)info==2)
- gotoout_set;
- /*Real-timesignalsmustbequeuedifsentbysigqueue,or
- someotherreal-timemechanism.Itisimplementation
- definedwhetherkill()doesso.Weattempttodoso,on
- theprincipleofleastsurprise,butsincekillisnot
- allowedtofailwithEAGAINwhenlowonmemorywejust
- makesureatleastonesignalgetsdeliveredanddont
- passontheinfostruct.*/
- q=__sigqueue_alloc(t,GFP_ATOMIC,(sig
- ((unsignedlong)info<2||
- info->si_code>=0)));//分配sigqueue結(jié)構(gòu)
- if(q){//如果成功分配到sigqueue結(jié)構(gòu),就把它添加到隊(duì)列中,并對(duì)其初始化
- list_add_tail(&q->list,&signals->list);
- switch((unsignedlong)info){
- case0:
- 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;
- case1:
- 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&&(unsignedlong)info!=1
- &&info->si_code!=SI_USER)
- /*
- *Queueoverflow,abort.Wemayabortifthesignalwasrt
- *andsentbyuserusingsomethingotherthankill().
- */
- return-EAGAIN;
- if(((unsignedlong)info>1)&&(info->si_code==SI_TIMER))
- /*
- *Setupareturntoindicatethatwedropped
- *thesignal.
- */
- ret=info->si_sys_private;
- }
- out_set:
- sigaddset(&signals->signal,sig);//設(shè)置信號(hào)位圖
- returnret;
- }
- /*
- *Tellaprocessthatithasanewactivesignal..
- *
- *NOTE!werelyonthepreviousspin_lockto
- *lockinterruptsforus!Wecanonlybecalledwith
- *"siglock"held,andthelocalinterruptmust
- *havebeendisabledwhenthatgotacquired!
- *
- *Noneedtosetneed_reschedsincesignaleventpassing
- *goesthrough->blocked
- */
- voidsignal_wake_up(structtask_struct*t,intresume)
- {
- unsignedintmask;
- set_tsk_thread_flag(t,TIF_SIGPENDING);//為進(jìn)程設(shè)置TIF_SIGPENDING標(biāo)志
- /*
- *ForSIGKILL,wewanttowakeitupinthestopped/tracedcase.
- *Wedontcheckt->stateherebecausethereisaracewithit
- *executinganotherprocessorandjustnowenteringstoppedstate.
- *Byusingwake_up_state,weensuretheprocesswillwakeupand
- *handleitsdeathsignal.
- */
- mask=TASK_INTERRUPTIBLE;
- if(resume)
- mask|=TASK_STOPPED|TASK_TRACED;
- if(!wake_up_state(t,mask))
- kick_process(t);
- }
此后當(dāng)該進(jìn)程被調(diào)度時(shí),在進(jìn)程返回用戶空間前,會(huì)調(diào)用do_notify_resume()處理該進(jìn)程的信號(hào)。
評(píng)論