linux內(nèi)核中的信號(hào)機(jī)制--信號(hào)處理
CPU architecture:ARM920T
本文引用地址:http://m.butianyuan.cn/article/201611/320006.htmAuthor:ce123(http://blog.csdn.net/ce123)
當(dāng)進(jìn)程被調(diào)度時(shí),會(huì)調(diào)用do_notify_resume()來處理信號(hào)隊(duì)列中的信號(hào)。信號(hào)處理主要就是調(diào)用sighand_struct結(jié)構(gòu)中對(duì)應(yīng)的信號(hào)處理函數(shù)。do_notify_resume()(arch/arm/kernel/signal.c)函數(shù)的定義如下:
- asmlinkagevoid
- do_notify_resume(structpt_regs*regs,unsignedintthread_flags,intsyscall)
- {
- if(thread_flags&_TIF_SIGPENDING)
- do_signal(¤t->blocked,regs,syscall);
- }
- /*
- *Notethatinitisaspecialprocess:itdoesntgetsignalsitdoesnt
- *wanttohandle.ThusyoucannotkillinitevenwithaSIGKILLevenby
- *mistake.
- *
- *Notethatwegothroughthesignalstwice:oncetocheckthesignalsthat
- *thekernelcanhandle,andthenwebuildalltheuser-levelsignalhandling
- *stack-framesinonegoafterthat.
- */
- staticintdo_signal(sigset_t*oldset,structpt_regs*regs,intsyscall)
- {
- structk_sigactionka;
- siginfo_tinfo;
- intsignr;
- /*
- *Wewantthecommoncasetogofast,which
- *iswhywemayincertaincasesgetherefrom
- *kernelmode.Justreturnwithoutdoinganything
- *ifso.
- */
- if(!user_mode(regs))//regs保存的是進(jìn)入內(nèi)核態(tài)之前的寄存器現(xiàn)場,必須為用戶模式,否則直接返回
- return0;
- if(try_to_freeze())
- gotono_signal;
- if(current->ptrace&PT_SINGLESTEP)
- ptrace_cancel_bpt(current);//和調(diào)試相關(guān),我們?cè)诤竺娴奈恼轮袝?huì)具體分析
- signr=get_signal_to_deliver(&info,&ka,regs,NULL);//取出等待處理的信號(hào)
- if(signr>0){
- handle_signal(signr,&ka,&info,oldset,regs,syscall);//處理信號(hào)
- if(current->ptrace&PT_SINGLESTEP)
- ptrace_set_bpt(current);
- return1;
- }
- no_signal:
- /*
- *Nosignaltodelivertotheprocess-restartthesyscall.
- */
- if(syscall){
- if(regs->ARM_r0==-ERESTART_RESTARTBLOCK){
- if(thumb_mode(regs)){
- regs->ARM_r7=__NR_restart_syscall;
- regs->ARM_pc-=2;
- }else{
- u32__user*usp;
- regs->ARM_sp-=12;
- usp=(u32__user*)regs->ARM_sp;
- put_user(regs->ARM_pc,&usp[0]);
- /*swi__NR_restart_syscall*/
- put_user(0xef000000|__NR_restart_syscall,&usp[1]);
- /*ldrpc,[sp],#12*/
- put_user(0xe49df00c,&usp[2]);
- flush_icache_range((unsignedlong)usp,
- (unsignedlong)(usp+3));
- regs->ARM_pc=regs->ARM_sp+4;
- }
- }
- if(regs->ARM_r0==-ERESTARTNOHAND||
- regs->ARM_r0==-ERESTARTSYS||
- regs->ARM_r0==-ERESTARTNOINTR){
- restart_syscall(regs);
- }
- }
- if(current->ptrace&PT_SINGLESTEP)
- ptrace_set_bpt(current);
- return0;
- }
執(zhí)行do_signal()函數(shù)時(shí),進(jìn)程一定處于內(nèi)核空間,通常進(jìn)程只有通過中斷或者系統(tǒng)調(diào)用才能進(jìn)入內(nèi)核空間,regs保存著系統(tǒng)調(diào)用或者中斷時(shí)的現(xiàn)場。user_mode()根據(jù)regs中的cpsr寄存器來判斷是中斷現(xiàn)場環(huán)境還是用戶態(tài)環(huán)境。如果不是用戶態(tài)環(huán)境,就不對(duì)信號(hào)進(jìn)行任何處理,直接從do_signal()函數(shù)返回。
如果user_mode()函數(shù)發(fā)現(xiàn)regs的現(xiàn)場是內(nèi)核態(tài),那就意味著這不是一次系統(tǒng)調(diào)用的返回,也不是一次普通的中斷返回,而是一次嵌套中斷返回(或者在系統(tǒng)調(diào)用過程中發(fā)生了中斷)。此時(shí)大概的執(zhí)行路徑應(yīng)該是這樣的:假設(shè)進(jìn)場現(xiàn)在運(yùn)行在用戶態(tài),此時(shí)發(fā)生一次中斷,進(jìn)場進(jìn)入內(nèi)核態(tài)(此時(shí)user_mode(regs)返回1,意味著中斷現(xiàn)場是用戶態(tài)。),此后在中斷返回前,發(fā)生了一個(gè)更高優(yōu)先級(jí)的中斷,于是CPU開始執(zhí)行高優(yōu)先級(jí)的處理函數(shù)(此時(shí)user_mode(regs)返回0,意味著中斷現(xiàn)場是在內(nèi)核態(tài))。當(dāng)高優(yōu)先級(jí)中斷處理結(jié)束后,在它返回時(shí),是不應(yīng)該處理信號(hào)的,因?yàn)樾盘?hào)的優(yōu)先級(jí)比中斷的優(yōu)先級(jí)低。在這種情況下,對(duì)信號(hào)的處理將會(huì)延遲到低優(yōu)先級(jí)中斷處理結(jié)束之后。相對(duì)于Windows內(nèi)核來說,盡管linux內(nèi)核中沒有一組顯式的操作函數(shù)來實(shí)現(xiàn)這一系列的優(yōu)先級(jí)管理方案,但是linux內(nèi)核和Windows內(nèi)核都使用了同樣的機(jī)制,優(yōu)先級(jí)關(guān)系為:高優(yōu)先級(jí)中斷->低優(yōu)先級(jí)中斷->軟中斷(類似Windows內(nèi)中的DPC)->信號(hào)(類似Windows內(nèi)核中的APC)->進(jìn)程運(yùn)行。
如果user_mode(regs)返回1,接下來會(huì)執(zhí)行(中間略去一下和本文關(guān)系不大的代碼)get_signal_to_deliver(),這個(gè)函數(shù)從當(dāng)前進(jìn)程的信號(hào)隊(duì)列(保存Private Signal Queue和Shared Signal Queue)取出等待處理的信號(hào)(調(diào)用dequeue_signal()函數(shù)),然后根據(jù)信號(hào)定位到對(duì)應(yīng)的signal_struct結(jié)構(gòu),如果信號(hào)的處理函數(shù)sa_handler為SIG_IGN,就忽略該信號(hào),繼續(xù)取下一個(gè)信號(hào);如果信號(hào)的處理函數(shù)sa_handler為SIG_DFL,意味著按照信號(hào)默認(rèn)的處理方式對(duì)待就可以了(例如直接調(diào)用do_coredump()等)。
如果get_signal_to_deliver()函數(shù)返回值大于0,說明這個(gè)信號(hào)的處理函數(shù)是在用戶態(tài)空間(通過signal()和sigaction()等函數(shù)設(shè)置的自定義信號(hào)處理函數(shù)。),將調(diào)用handle_signal()函數(shù)進(jìn)行處理。handle_signal()函數(shù)的定義如下:
- /*
- *OK,wereinvokingahandler
- */
- staticvoid
- handle_signal(unsignedlongsig,structk_sigaction*ka,
- siginfo_t*info,sigset_t*oldset,
- structpt_regs*regs,intsyscall)
- {
- structthread_info*thread=current_thread_info();
- structtask_struct*tsk=current;
- intusig=sig;
- intret;
- /*
- *Ifwewerefromasystemcall,checkforsystemcallrestarting...
- */
- if(syscall){
- switch(regs->ARM_r0){
- case-ERESTART_RESTARTBLOCK:
- case-ERESTARTNOHAND:
- regs->ARM_r0=-EINTR;
- break;
- case-ERESTARTSYS:
- if(!(ka->sa.sa_flags&SA_RESTART)){
- regs->ARM_r0=-EINTR;
- break;
- }
- /*fallthrough*/
- case-ERESTARTNOINTR:
- restart_syscall(regs);
- }
- }
- /*
- *translatethesignal
- */
- if(usig<32&&thread->exec_domain&&thread->exec_domain->signal_invmap)
- usig=thread->exec_domain->signal_invmap[usig];
- /*
- *Setupthestackframe//設(shè)置棧幀
- */
- if(ka->sa.sa_flags&SA_SIGINFO)
- ret=setup_rt_frame(usig,ka,info,oldset,regs);
- else
- ret=setup_frame(usig,ka,oldset,regs);
- /*
- *Checkthattheresultingregistersareactuallysane.
- */
- ret|=!valid_user_regs(regs);
- /*
- *Blockthesignalifwewereunsuccessful.
- */
- if(ret!=0){
- spin_lock_irq(&tsk->sighand->siglock);
- sigorsets(&tsk->blocked,&tsk->blocked,
- &ka->sa.sa_mask);
- if(!(ka->sa.sa_flags&SA_NODEFER))
- sigaddset(&tsk->blocked,sig);
- recalc_sigpending();
- spin_unlock_irq(&tsk->sighand->siglock);
- }
- if(ret==0)
- return;
- force_sigsegv(sig,tsk);
- }
1.臨時(shí)的用戶態(tài)堆棧在哪里呢?這個(gè)很好解決,因?yàn)榭梢灾苯邮褂眠M(jìn)程現(xiàn)有的用戶態(tài)堆棧,這里要保證的是:使用結(jié)束后這個(gè)堆棧必須和使用前是一模一樣的。
2.臨時(shí)堆棧解決后,需要確定的是通過什么方法來保證返回到用戶態(tài)后,進(jìn)程執(zhí)行的是信號(hào)的處理函數(shù)。我們知道在進(jìn)入內(nèi)核態(tài)時(shí),內(nèi)核態(tài)堆棧中保存了一個(gè)中斷現(xiàn)場,也就是一個(gè)pt_regs結(jié)構(gòu),中斷返回地址就保存在pt_regts中的pc中,因此我們這里只要把當(dāng)前進(jìn)程的pt_regs中pc設(shè)置為sa_handler,然后返回到用戶態(tài)就開始從sa_handler處開始執(zhí)行了。
- unsignedlonghandler=(unsignedlong)ka->sa.sa_handler;
- regs->ARM_pc=handler;
3.當(dāng)信號(hào)的用戶態(tài)處理函數(shù)執(zhí)行結(jié)束時(shí),需要再次進(jìn)入內(nèi)核態(tài),還原用戶態(tài)堆棧,并且修改pt_regs中的pc,保證將來能夠按照正常的方式返回用戶態(tài)。我們知道進(jìn)程要主動(dòng)進(jìn)入內(nèi)核態(tài)只有通過系統(tǒng)調(diào)用,出發(fā)異常等方法,為此內(nèi)核專門提供了一個(gè)系統(tǒng)調(diào)用sys_sigreturn()(還有一個(gè)sys_rt_sigreturn()),但是如何調(diào)用sys_sigreturn()呢?強(qiáng)制安裝信號(hào)處理函數(shù)最后必須調(diào)用一個(gè)sigreturn()不是一個(gè)好辦法,因?yàn)椴涣私鈨?nèi)核的程序員會(huì)對(duì)這個(gè)限制感到不解,為此程序員常常忘記在它們的信號(hào)處理函數(shù)的末尾調(diào)用sigreturn(),如果真是這樣,arm-linux-gcc也檢測(cè)不出這個(gè)錯(cuò)誤。為此,內(nèi)核修改regs的ARM_lr值,:
- regs->ARM_lr=retcode;
當(dāng)用戶態(tài)信號(hào)處理函數(shù)運(yùn)行結(jié)束時(shí),會(huì)從lr取出返回地址,因此內(nèi)核在構(gòu)建臨時(shí)regs時(shí),會(huì)把上面這段代碼的入口地址保存在lr,這樣當(dāng)信號(hào)處理完成后,就會(huì)順利的通過系統(tǒng)調(diào)用sys_sigreturn()進(jìn)入內(nèi)核。
4.當(dāng)通過構(gòu)造的sys_sigreturn()返回到內(nèi)核態(tài)之后,內(nèi)核需要順利的返回到用戶態(tài)執(zhí)行原來的代碼(信號(hào)處理前應(yīng)該返回的用戶空間狀態(tài)),但是此時(shí)進(jìn)入內(nèi)核空間的pt_regs上下文是通過sys_sigreturn()構(gòu)造的,而最初的內(nèi)核堆棧中的pt_regs上下文在第一次返回用戶空間執(zhí)行信號(hào)處理函數(shù)時(shí),就已經(jīng)被“銷毀”了(內(nèi)核態(tài)堆棧的pt_regs上下文在中斷返回后就不復(fù)存在了)。而現(xiàn)在必須通過最初的pt_regs上下文返回用戶態(tài),為此,在構(gòu)建臨時(shí)堆棧環(huán)境時(shí),內(nèi)核會(huì)把最初的pt_regs上下文備份到臨時(shí)堆棧中(位于用戶態(tài)堆棧),當(dāng)通過系統(tǒng)調(diào)用sys_sigreturn()再次進(jìn)入內(nèi)核時(shí),內(nèi)核從用戶態(tài)空間還原出原始的pt_regs。最后正常返回。
通過上面的討論,我們知道在這個(gè)迂回的處理過程中,關(guān)鍵在于用戶態(tài)的臨時(shí)堆棧環(huán)境的建立,這是一個(gè)sigframe結(jié)構(gòu):
- /*
- *Doasignalreturn;undothesignalstack.Thesearealignedto64-bit.
- */
- structsigframe{
- structsigcontextsc;//保存一組寄存器上下文
- unsignedlongextramask[_NSIG_WORDS-1];
- unsignedlongretcode;//保存返回地址
- structaux_sigframeaux__attribute__((aligned(8)));
- };
- structrt_sigframe{
- structsiginfo__user*pinfo;
- void__user*puc;
- structsiginfoinfo;
- structucontextuc;
- unsignedlongretcode;
- structaux_sigframeaux__attribute__((aligned(8)));
- };
- staticinlinevoid__user*
- get_sigframe(structk_sigaction*ka,structpt_regs*regs,intframesize)
- {
- unsignedlongsp=regs->ARM_sp;
- void__user*frame;
- /*
- *ThisistheX/Opensanctionedsignalstackswitching.
- */
- if((ka->sa.sa_flags&SA_ONSTACK)&&!sas_ss_flags(sp))
- sp=current->sas_ss_sp+current->sas_ss_size;
- /*
- *ATPCSB01mandates8-bytealignment
- */
- frame=(void__user*)((sp-framesize)&~7);
- /*
- *Checkthatwecanactuallywritetothesignalframe.
- */
- if(!access_ok(VERIFY_WRITE,frame,framesize))
- frame=NULL;
- returnframe;
- }
通過上面的討論,我們?cè)倩氐絛o_signal()中來,如果有用戶態(tài)的信號(hào)處理函數(shù),do_signal()會(huì)調(diào)用handle_signal(),handle_signal()將調(diào)用setup_frame()或者setup_rt_frame()來完成實(shí)際的工作,這里我們以setup_frame()為例進(jìn)行進(jìn)一步討論。
- staticint
- setup_frame(intusig,structk_sigaction*ka,sigset_t*set,structpt_regs*regs)
- {
- //在用戶態(tài)堆棧上分配一個(gè)sigframe結(jié)構(gòu)
- structsigframe__user*frame=get_sigframe(ka,regs,sizeof(*frame));
- interr=0;
- if(!frame)
- return1;
- //把相關(guān)信息從內(nèi)核態(tài)備份到用戶態(tài)堆棧的sigframe結(jié)構(gòu)中
- err|=setup_sigcontext(&frame->sc,&frame->aux,regs,set->sig[0]);
- if(_NSIG_WORDS>1){
- err|=__copy_to_user(frame->extramask,&set->sig[1],
- sizeof(frame->extramask));
- }
- if(err==0)
- err=setup_return(regs,ka,&frame->retcode,frame,usig);
- returnerr;
- }
- staticint
- setup_return(structpt_regs*regs,structk_sigaction*ka,
- unsignedlong__user*rc,void__user*frame,intusig)
- {
- unsignedlonghandler=(unsignedlong)ka->sa.sa_handler;
- unsignedlongretcode;
- intthumb=0;
- unsignedlongcpsr=regs->ARM_cpsr&~PSR_f;
- /*
- *Maybeweneedtodelivera32-bitsignaltoa26-bittask.
- */
- if(ka->sa.sa_flags&SA_THIRTYTWO)
- cpsr=(cpsr&~MODE_MASK)|USR_MODE;
- #ifdefCONFIG_ARM_THUMB
- if(elf_hwcap&HWCAP_THUMB){
- /*
- *TheLSBofthehandlerdeterminesifweregoingto
- *beusingTHUMBorARMmodeforthissignalhandler.
- */
- thumb=handler&1;
- if(thumb)
- cpsr|=PSR_T_BIT;
- else
- cpsr&=~PSR_T_BIT;
- }
- #endif
//這里的retcode就是保存手工構(gòu)造的sigreturn()代碼 - if(ka->sa.sa_flags&SA_RESTORER){
- retcode=(unsignedlong)ka->sa.sa_restorer;
- }else{
- unsignedintidx=thumb;
- if(ka->sa.sa_flags&SA_SIGINFO)
- idx+=2;
- if(__put_user(sigreturn_codes[idx],rc))
- return1;
- if(cpsr&MODE32_BIT){
- /*
- *32-bitcodecanusethenewhigh-page
- *signalreturncodesupport.
- */
- retcode=KERN_SIGRETURN_CODE+(idx<<2)+thumb;
- }else{
- /*
- *Ensurethattheinstructioncachesees
- *thereturncodewrittenontothestack.
- */
- flush_icache_range((unsignedlong)rc,
- (unsignedlong)(rc+1));
- retcode=((unsignedlong)rc)+thumb;
- }
- }
- regs->ARM_r0=usig;
- regs->ARM_sp=(unsignedlong)frame;//堆棧
- regs->ARM_lr=retcode;//返回地址,當(dāng)用戶態(tài)信號(hào)處理函數(shù)結(jié)束時(shí),就會(huì)把這個(gè)地址作為返回地址
- regs->ARM_pc=handler;//信號(hào)處理函數(shù)
- regs->ARM_cpsr=cpsr;
- return0;
- }
當(dāng)setup_frame()返回后,一切準(zhǔn)備就緒,因此可以從內(nèi)核態(tài)返回了,這樣就順利過渡到用戶態(tài)的信號(hào)處理函數(shù)。當(dāng)這個(gè)函數(shù)處理結(jié)束后,會(huì)通過retcode再次進(jìn)入內(nèi)核態(tài),現(xiàn)在我們看看retcode是如何處理的,下面的代碼選自glibc(2.3.2):
- #include
- /*IfnoSA_RESTORERfunctionwasspecifiedbytheapplicationweuse
- oneofthese.Thisavoidstheneedforthekerneltosynthesiseareturn
- instructiononthestack,whichwouldinvolveexpensivecacheflushes.*/
- ENTRY(__default_sa_restorer)
- swiSYS_ify(sigreturn)
- #ifdef__NR_rt_sigreturn
- ENTRY(__default_rt_sa_restorer)
- swiSYS_ify(rt_sigreturn)
- #defineSYS_ify(syscall_name)(__NR_##syscall_name)
下面具體看看sys_sigreturn()的定義:
- asmlinkageintsys_sigreturn(structpt_regs*regs)
- {
- structsigframe__user*frame;
- sigset_tset;
- /*Alwaysmakeanypendingrestartedsystemcallsreturn-EINTR*/
- current_thread_info()->restart_block.fn=do_no_restart_syscall;
- /*
- *Sincewestackedthesignalona64-bitboundary,
- *thenspshouldbewordalignedhere.Ifits
- *not,thentheuseristryingtomesswithus.
- */
- if(regs->ARM_sp&7)
- gotobadframe;
- frame=(structsigframe__user*)regs->ARM_sp;
- if(!access_ok(VERIFY_READ,frame,sizeof(*frame)))
- gotobadframe;
- if(__get_user(set.sig[0],&frame->sc.oldmask)
- ||(_NSIG_WORDS>1
- &&__copy_from_user(&set.sig[1],&frame->extramask,
- sizeof(frame->extramask))))
- gotobadframe;
- sigdelsetmask(&set,~_BLOCKABLE);
- spin_lock_irq(¤t->sighand->siglock);
- current->blocked=set;
- recalc_sigpending();
- spin_unlock_irq(¤tt->sighand->siglock);
- if(restore_sigcontext(regs,&frame->sc,&frame->aux))
- gotobadframe;
- /*SendSIGTRAPifweresingle-stepping*/
- if(current->ptrace&PT_SINGLESTEP){
- ptrace_cancel_bpt(current);
- send_sig(SIGTRAP,current,1);
- }
- returnregs->ARM_r0;
- badframe:
- force_sig(SIGSEGV,current);
- return0;
- }
- staticint
- restore_sigcontext(structpt_regs*regs,structsigcontext__user*sc,
- structaux_sigframe__user*aux)
- {
- interr=0;
- __get_user_error(regs->ARM_r0,&sc->arm_r0,err);
- __get_user_error(regs->ARM_r1,&sc->arm_r1,err);
- __get_user_error(regs->ARM_r2,&sc->arm_r2,err);
- __get_user_error(regs->ARM_r3,&sc->arm_r3,err);
- __get_user_error(regs->ARM_r4,&sc->arm_r4,err);
- __get_user_error(regs->ARM_r5,&sc->arm_r5,err);
- __get_user_error(regs->ARM_r6,&sc->arm_r6,err);
- __get_user_error(regs->ARM_r7,&sc->arm_r7,err);
- __get_user_error(regs->ARM_r8,&sc->arm_r8,err);
- __get_user_error(regs->ARM_r9,&sc->arm_r9,err);
- __get_user_error(regs->ARM_r10,&sc->arm_r10,err);
- __get_user_error(regs->ARM_fp,&sc->arm_fp,err);
- __get_user_error(regs->ARM_ip,&sc->arm_ip,err);
- __get_user_error(regs->ARM_sp,&sc->arm_sp,err);
- __get_user_error(regs->ARM_lr,&sc->arm_lr,err);
- __get_user_error(regs->ARM_pc,&sc->arm_pc,err);
- __get_user_error(regs->ARM_cpsr,&sc->arm_cpsr,err);
- err|=!valid_user_regs(regs);
- #ifdefCONFIG_IWMMXT
- if(err==0&&test_thread_flag(TIF_USING_IWMMXT))
- err|=restore_iwmmxt_context(&aux->iwmmxt);
- #endif
- #ifdefCONFIG_VFP
- //if(err==0)
- //err|=vfp_restore_state(&aux->vfp);
- #endif
- returnerr;
- }
評(píng)論