Kernel version:2.6.14
CPU architecture:ARM920T
Author:ce123(http://blog.csdn.net/ce123)
当进程被调度时,会调用do_notify_resume()来处理信号行列中的信号。信号处理首要便是调用sighand_struct结构中对应的信号处理函数。do_notify_resume()(arch/arm/kernel/signal.c)函数的界说如下:
print?
- asmlinkagevoid
- do_notify_resume(structpt_regs*regs,unsignedintthread_flags,intsyscall)
- {
- if(thread_flags&_TIF_SIGPENDING)
- do_signal(¤t->blocked,regs,syscall);
- }
_TIF_SIGPENDING标志是在signal_wake_up()函数中设置的,查看该标志后,接下来就调用do_signal()函数,咱们来看看do_signal()(arch/arm/kernel/signal.c)的详细界说:
print?
- /*
- *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保存的是进入内核态之前的寄存器现场,有必要为用户形式,不然直接回来
- return0;
- if(try_to_freeze())
- gotono_signal;
- if(current->ptrace&PT_SINGLESTEP)
- ptrace_cancel_bpt(current);//和调试相关,咱们在后面的文章中会详细分析
- signr=get_signal_to_deliver(&info,&ka,regs,NULL);//取出等候处理的信号
- if(signr>0){
- handle_signal(signr,&ka,&info,oldset,regs,syscall);//处理信号
- 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;
- }
履行do_signal()函数时,进程必定处于内核空间,一般进程只要经过中止或许体系调用才干进入内核空间,regs保存着体系调用或许中止时的现场。user_mode()依据regs中的cpsr寄存器来判别是中止现场环境仍是用户态环境。假如不是用户态环境,就不对信号进行任何处理,直接从do_signal()函数回来。
假如user_mode()函数发现regs的现场是内核态,那就意味着这不是一次体系调用的回来,也不是一次一般的中止回来,而是一次嵌套中止回来(或许在体系调用进程中产生了中止)。此刻大约的履行途径应该是这样的:假定出场现在运转在用户态,此刻产生一次中止,出场进入内核态(此刻user_mode(regs)回来1,意味着中止现场是用户态。),尔后在中止回来前,产生了一个更高优先级的中止,所以CPU开端履行高优先级的处理函数(此刻user_mode(regs)回来0,意味着中止现场是在内核态)。当高优先级中止处理完毕后,在它回来时,是不应该处理信号的,由于信号的优先级比中止的优先级低。在这种情况下,对信号的处理将会延迟到低优先级中止处理完毕之后。相对于Windows内核来说,虽然linux内核中没有一组显式的操作函数来完结这一系列的优先级办理计划,可是linux内核和Windows内核都运用了相同的机制,优先级关系为:高优先级中止->低优先级中止->软中止(相似Windows内里的DPC)->信号(相似Windows内核中的APC)->进程运转。
假如user_mode(regs)回来1,接下来会履行(中心省略一下和本文关系不大的代码)get_signal_to_deliver(),这个函数从当时进程的信号行列(保存Private Signal Queue和Shared Signal Queue)取出等候处理的信号(调用dequeue_signal()函数),然后依据信号定位到对应的signal_struct结构,假如信号的处理函数sa_handler为SIG_IGN,就疏忽该信号,持续取下一个信号;假如信号的处理函数sa_handler为SIG_DFL,意味着依照信号默许的处理办法对待就能够了(例如直接调用do_coredump()等)。
假如get_signal_to_deliver()函数回来值大于0,阐明这个信号的处理函数是在用户态空间(经过signal()和sigaction()等函数设置的自界说信号处理函数。),将调用handle_signal()函数进行处理。handle_signal()函数的界说如下:
print?
- /*
- *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//设置栈帧
- */
- 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.暂时的用户态仓库在哪里呢?这个很好处理,由于能够直接运用进程现有的用户态仓库,这儿要确保的是:运用完毕后这个仓库有必要和运用前是如出一辙的。
2.暂时仓库处理后,需求确认的是经过什么办法来确保回来到用户态后,进程履行的是信号的处理函数。咱们知道在进入内核态时,内核态仓库中保存了一个中止现场,也便是一个pt_regs结构,中止回来地址就保存在pt_regts中的pc中,因而咱们这儿只要把当时进程的pt_regs中pc设置为sa_handler,然后回来到用户态就开端从sa_handler处开端履行了。
print?
- unsignedlonghandler=(unsignedlong)ka->sa.sa_handler;
- regs->ARM_pc=handler;
3.当信号的用户态处理函数履行完毕时,需求再次进入内核态,复原用户态仓库,而且修正pt_regs中的pc,确保将来能够依照正常的办法回来用户态。咱们知道进程要自动进入内核态只要经过体系调用,动身反常等办法,为此内核专门供给了一个体系调用sys_sigreturn()(还有一个sys_rt_sigreturn()),可是怎么调用sys_sigreturn()呢?强制装置信号处理函数最终有必要调用一个sigreturn()不是一个好办法,由于不了解内核的程序员会对这个约束感到不解,为此程序员常常忘记在它们的信号处理函数的结尾调用sigreturn(),假如真是这样,arm-linux-gcc也检测不出这个过错。为此,内核修正regs的ARM_lr值,:
print?
- regs->ARM_lr=retcode;
当用户态信号处理函数运转完毕时,会从lr取出回来地址,因而内核在构建暂时regs时,会把上面这段代码的进口地址保存在lr,这样当信号处理完结后,就会顺畅的经过体系调用sys_sigreturn()进入内核。
4.当经过结构的sys_sigreturn()回来到内核态之后,内核需求顺畅的回来到用户态履行本来的代码(信号处理前应该回来的用户空间状况),可是此刻进入内核空间的pt_regs上下文是经过sys_sigreturn()结构的,而开始的内核仓库中的pt_regs上下文在第一次回来用户空间履行信号处理函数时,就现已被“毁掉”了(内核态仓库的pt_regs上下文在中止回来后就不复存在了)。而现在有必要经过开始的pt_regs上下文回来用户态,为此,在构建暂时仓库环境时,内核会把开始的pt_regs上下文备份到暂时仓库中(坐落用户态仓库),当经过体系调用sys_sigreturn()再次进入内核时,内核从用户态空间复原出原始的pt_regs。最终正常回来。
经过上面的评论,咱们知道在这个迂回的处理进程中,关键在于用户态的暂时仓库环境的树立,这是一个sigframe结构:
print?
- /*
- *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)));
- };
其间的sigcontext的效果相似于pt_regs用于保存相关寄存器上下文,原始的pt_regs的相关信息就备份在这儿。而整个sigframe结构是经过get_sigframe()函数在进程用户态空间分配的,get_sigframe()界说如下:
print?
- 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;
- }
get_sigframe()经过用户态空间仓库的sp-sizeof(struct sigframe)在用户态仓库的顶部分配了一篇存储空间,将来运用完结后,再经过sp+sizeof(struct sigframe)复原。
经过上面的评论,咱们再回到do_signal()中来,假如有用户态的信号处理函数,do_signal()会调用handle_signal(),handle_signal()将调用setup_frame()或许setup_rt_frame()来完结实践的作业,这儿咱们以setup_frame()为例进行进一步评论。
print?
- staticint
- setup_frame(intusig,structk_sigaction*ka,sigset_t*set,structpt_regs*regs)
- {
- //在用户态仓库上分配一个sigframe结构
- structsigframe__user*frame=get_sigframe(ka,regs,sizeof(*frame));
- interr=0;
- if(!frame)
- return1;
- //把相关信息从内核态备份到用户态仓库的sigframe结构中
- 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;
- }
setup_return()设置回来地址,其界说如下:
print?
- 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便是保存手艺结构的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;//回来地址,当用户态信号处理函数完毕时,就会把这个地址作为回来地址
- regs->ARM_pc=handler;//信号处理函数
- regs->ARM_cpsr=cpsr;
- return0;
- }
当setup_frame()回来后,全部准备就绪,因而能够从内核态回来了,这样就顺畅过渡到用户态的信号处理函数。当这个函数处理完毕后,会经过retcode再次进入内核态,现在咱们看看retcode是怎么处理的,下面的代码选自glibc(2.3.2):
print?
- #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()的界说:
print?
- 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;
- }
这个函数首要调用restore_sigcontext()依据用户态态仓库上的sigframe备份复原pt_regs。
print?
- 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;
- }
restore_sigcontext()很简单,当它回来后,经过sys_sigreturn()在内核态仓库树立的pt_regs现已变为调用信号处理函数之前的pt_regs。这样,sys_sigreturn()回来用户态时,就顺畅地过渡到处理之前的状况了。