1、Linux内核源码分析之内核源码分析之 信号传递信号传递郭海林郭海林2012.11.3信号的处理阶段 信号的产生内核更新目标进程的数据结构更新目标进程的数据结构以表示新信号以被发送 信号的传递内核强迫目标进程对信号做出反应对信号做出反应与信号机制相关的数据结构信号响应的三种方式 显示地忽略信号 执行与信号相关的缺省操作 Terminate:进程被终止 Dump:Terminate+Core Ignore:忽略 Stop:进程被停止,即置为TASK_STOPPED Continue:置为TASK_RUNNING 通过调用相应的信号处理函数捕获信号(&sighand-actionnr-1)-sa.
2、sa_handler信号检测和响应时机 当前进程由于系统调用、中断或异常而进入系统空间以后,从系统空间返回到用户空间的前夕。当前进程在内核中进入睡眠以后刚被唤醒的时候(必定是在系统调用中),或者由于不可忽略信号的存在而提前返回到用户空间。do_signal()系统调用的重新执行系统调用的重新执行get_signal_to_deliver()for(;)/取出一个未加屏蔽的信号signr=dequeue_signal(current,¤t-blocked,info);/没有可处理信号,退出for循环if(!signr)break;/取得信号向量表的表项ka=&sighand-acti
3、onsignr-1;/根据ka-sa.sa_handler的值进行处理信号/A.显示忽略,继续寻找下一个挂起信号显示忽略,继续寻找下一个挂起信号if(ka-sa.sa_handler=SIG_IGN)continue;get_signal_to_deliver()/B.用户自定义的处理函数,跳出用户自定义的处理函数,跳出break,返回到,返回到do_signal()if(ka-sa.sa_handler!=SIG_DFL)*return_ka=*ka;if(ka-sa.sa_flags&SA_ONESHOT)/如果设置了标志SA_ONESHOTka-sa.sa_handler=SIG_DFL
4、;break;/*will return non-zero signr value*/C.如果信号处理为默认动作如果信号处理为默认动作/C1.为Ignore,如SIGCHLD,SIGCONT,SIGURG,SIGWINCH等信号,返回forif(sig_kernel_ignore(signr)/*Default is nothing.*/continue;/C2.为Stop,如SIGSTOP,SIGTSTP,SIGTIN,SIGTOU等信号,if(sig_kernel_stop(signr)./会调用schedule()切换进程continue;get_signal_to_deliver()/
5、C3.为Terminate+Core,如SIGQUIT,SIGILL,SIGTRAP,SIGABRT等等信号,执行到C4if(sig_kernel_coredump(signr).do_coredump(info-si_signo,info-si_signo,regs);/C4.为terminate,如SIGXCPU,SIGPROF,SIGPWR,SIGUNUSED等等信号,结束进程do_group_exit(info-si_signo);/end of for(;)从get_signal_to_deliver()函数返回的情形:没有非阻塞的信号可选择选择了一个用户自定义处理的信号捕获信号机制
6、l handle_signal()运行在内核态l 用户定义的信号处理函数运行在用户态l 在当前进程恢复“正常”执行前,必须先执行用户态的信号处理程序整个过程 在用户态堆栈为信号处理程序预设一个frame,把内核栈“原始frame”保存到此。内核插入对系统调用sigreturn的代码。修改内核栈中“原始frame”修改成为执行信号处理程序所需的frame。返回到用户空间,但执行的是信号处理程序。信号处理程序执行完之后,通过sigreturn返回到内核空间。在系统调用sigreturn中从用户态堆栈恢复“原始frame”。再从内核态返回用户态,继续执行原始的用户程序。handle_signal()
7、-setup_rt_frame()A.在用户态堆栈上分配一个frame,用来保存内核堆栈上“原始frame”分配分配frame:struct sigframe*frame=get_sigframe(ka,regs,sizeof(*frame),&fpstate);unsigned long sp=regs-sp;sp=align_sigframe(sp-frame_size);return(void _user*)sp;示意图(1)espframesigframeretcodeextramaskfpstatescsigpretcodesetup_rt_frame()B.填充struct sig
8、frame _put_user(sig,&frame-sig)ia32_setup_sigcontext(&frame-sc,fpstate,regs,set-sig0)_copy_to_user(frame-extramask,&set-sig1,sizeof(frame-extramask)put_user_ex(ptr_to_compat(restorer),&frame-pretcode);put_user_ex(*(u64*)&code),(u64*)frame-retcode);示意图(2)espframesigframeretcodeextramaskfpstatescsigpr
9、etcodeint 0 x80;movl 119,%eax;popl%eaxsetup_rt_frame()C.修改内核堆栈原来frameregs-sp=(unsigned long)frame;regs-ip=(unsigned long)ka-sa.sa_handler;/*Make-mregparm=3 work*/regs-ax=sig;regs-dx=0;regs-cx=0;loadsegment(ds,_USER32_DS);loadsegment(es,_USER32_DS);regs-cs=_USER32_CS;regs-ss=_USER32_DS;示意图(3)espframe
10、sigframeretcodeextramaskfpstatescsigpretcodeint 0 x80;movl 119,%eax;popl%eax内核态-用户态 D.执行信号处理程序示意图(4)sigframeretcodeextramaskfpstatescsigpretcodeint 0 x80;movl 119,%eax;popl%eaxespeip信号处理程序执行ret指令:popl%eip用户态-内核态 E.系统调用sigreturn 重回内核态示意图(5)sigframeretcodeextramaskfpstatescsigpretcodeint 0 x80;movl 11
11、9,%eax;popl%eaxeipespsys_sigreturn()F.恢复内核态的原始frame struct sigframe*frame=(struct sigframe*)(regs-sp-8);_copy_from_user(&set.sig1,&frame-extramask,sizeof(frame-extramask);restore_sigcontext(regs,&frame-sc,&ax);示意图(6)sigframeretcodeextramaskfpstatescsigpretcodeint 0 x80;movl 119,%eax;popl%eaxeipespframe内核态-用户态 G.回到用户程序正常执行流程回顾 从内核的角度:怎样从A向B发送一个信号信号的发送 B怎样执行这个信号信号的传递 从应用程序的角度:怎样发送一个信号kill,tkill,tgkill.目标进程应该阻塞哪些信号sigprocmask 目标进程如何响应收到的信号singnal,sigactionThank U For Listening!Understanding the Linux KernelRead the fucking source code!