3、当信号的用户态处理函数执行结束时,需要再次进入内核态,还原用户态堆栈,并且修改pt_regs中的pc,保证将来能够按照正常的方式返回用户态。我们知道进程要主动进入内核态只有通过系统调用,出发异常等方法,为此内核专门提供了一个系统调用sys_sigreturn()(还有一个sys_rt_sigreturn()),但是如何调用sys_sigreturn()呢?强制安装信号处理函数后必须调用一个sigreturn()不是一个好办法,因为不了解内核的程序员会对这个限制感到不解,为此程序员常常忘记在它们的信号处理函数的末尾调用sigreturn(),如果真是这样,arm-linux-gcc也检测不出这个错误。为此,内核修改regs的ARM_lr值,:

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结构:

/*
 * Do a signal return; undo the signal stack.  These are aligned to 64-bit.
 */
struct sigframe {
 struct sigcontext sc;//保存一组寄存器上下文
 unsigned long extramask[_NSIG_WORDS-1];
 unsigned long retcode;//保存返回地址
 struct aux_sigframe aux __attribute__((aligned(8)));
};

struct rt_sigframe {
 struct siginfo __user *pinfo;
 void __user *puc;
 struct siginfo info;
 struct ucontext uc;
 unsigned long retcode;
 struct aux_sigframe aux __attribute__((aligned(8)));
};

  其中的sigcontext的作用类似于pt_regs用于保存相关寄存器上下文,原始的pt_regs的相关信息备份在这里。而整个sigframe结构是通过get_sigframe()函数在进程用户态空间分配的,get_sigframe()定义如下:

static inline void __user *
get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, int framesize)
{
 unsigned long sp = regs->ARM_sp;
 void __user *frame;

 /*
  * This is the X/Open sanctioned signal stack switching.
  */
 if ((ka->sa.sa_flags & SA_ONSTACK) && !sas_ss_flags(sp))
  sp = current->sas_ss_sp + current->sas_ss_size;

 /*
  * ATPCS B01 mandates 8-byte alignment
  */
 frame = (void __user *)((sp - framesize) & ~7);

 /*
  * Check that we can actually write to the signal frame.
  */
 if (!access_ok(VERIFY_WRITE, frame, framesize))
  frame = NULL;

 return frame;
}