一、问题
对于多线程的时候,为了避免堆栈被破坏之后堆栈已经不再有效,从而发生二次内存访问违例异常。有些任务会在自己的系统启动的时候注册一个信号专门使用的异常信号,但是对于多线程的时候,理论上就可以应该为每个线程都分配一个私有的信号堆栈。但是这样对于系统内存消耗会特别多,可能系统根本负担不起。有些系统一般是让所有的线程共享一个信号堆栈,这个时候当一个进程(其中有多个线程)获得了多个信号的时候这个系统的堆栈是如何处理的呢?
二、API及实现
sys32_sigaltstack(const stack_t32 __user *uss, stack_t32 __user *uoss)
if (uss) {
if (!access_ok(VERIFY_READ, uss, sizeof(*uss)))
return -EFAULT;
err |= __get_user(ss_sp, &uss->ss_sp);
err |= __get_user(kss.ss_size, &uss->ss_size);
err |= __get_user(kss.ss_flags, &uss->ss_flags);
if (err)
return -EFAULT;
kss.ss_sp = (void __user *) ss_sp;
}
这里有一个我们不太关注的ss_flags标志,这个可选的值为SS_DISABLE or SS_ONSTACK,也就是可以使能,也可以禁用该功能。正如该函数的API中说明的,当我们当前正在堆栈处理函数中,这个系统调用时会失败的:
do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long sp)
if (on_sig_stack(sp))
goto out;
…………
if (ss_flags != SS_DISABLE && ss_flags != SS_ONSTACK && ss_flags != 0)
goto out;
if (ss_flags == SS_DISABLE) {
ss_size = 0;
ss_sp = NULL;
} else {
error = -ENOMEM;
if (ss_size < MINSIGSTKSZ)
goto out;
}
current->sas_ss_sp = (unsigned long) ss_sp;
current->sas_ss_size = ss_size;
三、当异常发生时
handle_signal-->>setup_rt_frame-->>ia32_setup_rt_frame-->>get_sigframe
/* This is the X/Open sanctioned signal stack switching. */
if (ka->sa.sa_flags & SA_ONSTACK) {
if (sas_ss_flags(sp) == 0)
sp = current->sas_ss_sp + current->sas_ss_size;
}
/* True if we are on the alternate signal stack. */
static inline int on_sig_stack(unsigned long sp)
{
return (sp - current->sas_ss_sp < current->sas_ss_size);
}
static inline int sas_ss_flags(unsigned long sp)
{
return (current->sas_ss_size == 0 ? SS_DISABLE
: on_sig_stack(sp) ? SS_ONSTACK : 0);
}
这里做个一个特殊处理。就是判断如果当前系统的堆栈已经在 替换堆栈 中,那么我们就需要在当前堆栈的基础上继续执行。但是从这个结构可以看到,其中的sas_ss_size都是一个线程内部的直接变量,所以不同的线程不会相互影响。这个带来的效果就是如果所有的线程共享同一个堆栈,那么不同的线程信号处理函数会相互干扰,但是同一个线程却不会受到影响。