现在的位置: 首页 > 综合 > 正文

<深入浅出> linux 信号栈

2013年05月24日 ⁄ 综合 ⁄ 共 1266字 ⁄ 字号 评论关闭

 

以mips octeon为例,说明信号独立栈的运行机制。

用户态设置独立信号栈的操作如下:

char signal_stk[SIZE] = {0};

stack_t sigstk;

sigstk.ss_sp = signal_stk;
sigstk.ss_flags = SS_ONSTACK;

sigaltstack(&sigstk);

以上代码就会把用户态的信号栈,设定为用户提供的signal_stk数组。

内核所有的代码均在signal.c里。

中断和异常resume_userspace时,会检查是否有挂起的信号待处理,如果是,则do_signal,handle_signal依次被调用。而与信号栈处理相关的函数,位于handle_signal里面的setup_frame,即为用户态信号处理函数准备堆栈环境。由于eipb2单板没有配置CONFIG_TRAD_SIGNALS,故实际调用的是setup_rt_frame。该函数中,内核为了获取信号栈地址,调用了get_sigframe函数:

frame = get_sigframe(ka, regs, sizeof(*frame));

而这正是决定是否采用独立栈的地方。该函数接收用户态现场信息(即发生中断或者异常时的用户态寄存器现场),返回一个用户态的栈指针,至于这个栈是独立信号栈,还是进程自己的栈,则通过如下判断:

if ((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags (sp) == 0))

第一个判断条件的意思是用户态通过sigstack系统调用设置了独立栈标志;

第二个判断条件是,用户态进程栈,不在独立栈的范围内,即:

sp = current->sas_ss_sp + current->sas_ss_size;

其中,sas_ss_sp和sas_ss_size已经通过sigaltstack设定为用户指定的值,即上面用户态例子的signal_stk数组。

若满足条件的话,则返回用户数组(末尾减去帧长度)作为独立信号栈帧起始地址,否则,以用户进程本身的堆栈作为信号栈。

简化后的代码大意为:

sp = current->sas_ss_sp + current->sas_ss_size;

return (sp - frame_size)&(~15); //按16字节对齐

可以看出,在设置了独立栈后,内核给用户准备的栈帧,位于数组的末尾。

根据返回的栈帧,内核开始填充用户态数据(具体流程见深入理解内核第三版440页),其中最重要的几个字段就包括sigreturn系统调用指令(在octeon里,这两条指令是

0x24020000 + syscall和0x<?xml:namespace prefix = st1 />0000000c。在设置好该字段后,将此字段的地址作为参数,强行赋给用户态的ra,以便用户态信号处理函数返回后可以直接调用sigreturn,恢复现场。

7月10日增加信号处理流程说明:

用户态程序--->中断返回检查是否有挂起信号---->用户态信号处理函数---->返回内核态--->恢复用户态现场寄存器

 

抱歉!评论已关闭.