c-Linux中的signal和rt_signal系统调用之间有什么区别


我开发了一个处理SIGILL信号的库。因为我想避免对libc的依赖,并且直接使用Linux系统调用。我注意到我的库挂在一些Linux系统上,经过大量调试,我发现使用rt_sigaction系统调用而不是sigaction解决了这个问题。然而,我没有找到关于这两个系统调用之间差异的描述。SO上有人知道潜在的细节吗?

更新:我使用信号处理程序来检测CPU对某些ARM指令扩展的支持,例如XScale指令MIATT。这是指令探测功能:

static uint32_t probe_xscale() {
register uint32_t retValue asm("r0") = 0;
asm volatile (
// Equivalent of the following code:
//  ".arch xscalen"
//  "MIATT acc0, r0, r0;"
// If the next line raises SIGILL,  the signal handle will change r0 to 1 and skip the instruction (4 bytes)
"MCR P0, 0x1, r0, c15, c0, 0;"
: "+r" (retValue)
:
:
);
return retValue;
}

在SIGILL处理程序中,我将PC寄存器提前4个字节(此指令的大小),并更改其中一个寄存器以指示调用了SIGILL处理器。这是信号处理程序代码。

static void probe_signal_handler(int, siginfo_t *, void* ptr) {
ucontext_t* ctx = (ucontext_t*)ptr;
ctx->uc_mcontext.arm_pc += 4;
ctx->uc_mcontext.arm_r0 = 1;
}

以下是我进行探测的方法(如果指令没有导致SIGILL,则函数返回0,如果调用了SIGILL处理程序,则返回1,如果sigaction系统调用失败,则返回2):

static uint32_t probeInstruction(uint32_t (*ProbeFunction)()) {
struct sigaction oldSigillAction;
struct sigaction probeSigillAction;
memset(&probeSigillAction, 0, sizeof(probeSigillAction));
probeSigillAction.sa_sigaction = &probe_signal_handler;
// Needs Linux >= 2.2
probeSigillAction.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
int sigactionResult = _syscall_sigaction(SIGILL, &probeSigillAction, &oldSigillAction);
if (sigactionResult == 0) {
const uint32_t probeResult = ProbeFunction();
_syscall_sigaction(SIGILL, &oldSigillAction, NULL);
return probeResult;
} else {
return 2;
}
}

以下是我对sigaction系统调用存根函数的实现:

static int _syscall_sigaction(int signum, const struct sigaction *new_action, struct sigaction *old_action) __attribute__((noinline));
static int _syscall_sigaction(int signalNumberParameter, const struct sigaction *newActionParameter, struct sigaction *oldActionParameter) {
register int result asm ("r0");
register int signalNumber asm ("r0") = signalNumberParameter;
register const struct sigaction *newAction asm ("r1") = newActionParameter;
register struct sigaction *oldAction asm ("r2") = oldActionParameter;
register int syscallNumber asm ("r7") = __NR_rt_sigaction;
asm volatile (
"swi $0;"
: "=r" (result)
: "r" (signalNumber), "r" (newAction), "r" (oldAction), "r" (syscallNumber)
:
);
return result;
}

我在安卓SDK(qemu)的模拟器中测试了这段代码,并在运行Ubuntu的Pandboard上进行了测试。在模拟器中,代码运行良好(在模拟ARM9和Cortex-A8 CPU时),但在Pandboard上,如果我使用__NR_sigation,它会挂在MIATT指令上:似乎在信号处理程序之后,代码不会跳过4个字节,而是运行相同的指令。

我没有确切的答案,但我仍然会努力做出贡献:

查看内核来源:

300SYSCALL_DEFINE3(sigaction, int, sig, const struct sigaction __user *, act,
301        struct sigaction __user *, oact)
302{
303        struct k_sigaction new_ka, old_ka;
304        int ret;
305        int err = 0;
306
307        if (act) {
308                old_sigset_t mask;
309
310                if (!access_ok(VERIFY_READ, act, sizeof(*act)))
311                        return -EFAULT;
312                err |= __get_user(new_ka.sa.sa_handler, &act->sa_handler);
313                err |= __get_user(new_ka.sa.sa_flags, &act->sa_flags);
314                err |= __get_user(mask, &act->sa_mask.sig[0]);
315                if (err)
316                        return -EFAULT;
317
318                siginitset(&new_ka.sa.sa_mask, mask);
319        }
320
321        ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
322
323        if (!ret && oact) {
324                if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)))
325                        return -EFAULT;
326                err |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags);
327                err |= __put_user(old_ka.sa.sa_handler, &oact->sa_handler);
328                err |= __put_user(old_ka.sa.sa_mask.sig[0], oact->sa_mask.sig);
329                err |= __put_user(0, &oact->sa_mask.sig[1]);
330                err |= __put_user(0, &oact->sa_mask.sig[2]);
331                err |= __put_user(0, &oact->sa_mask.sig[3]);
332                if (err)
333                        return -EFAULT;
334        }
335
336        return ret;
337}
338#endif

与。

2955SYSCALL_DEFINE4(rt_sigaction, int, sig,
2956                const struct sigaction __user *, act,
2957                struct sigaction __user *, oact,
2958                size_t, sigsetsize)
2959{
2960        struct k_sigaction new_sa, old_sa;
2961        int ret = -EINVAL;
2962
2963        /* XXX: Don't preclude handling different sized sigset_t's.  */
2964        if (sigsetsize != sizeof(sigset_t))
2965                goto out;
2966
2967        if (act) {
2968                if (copy_from_user(&new_sa.sa, act, sizeof(new_sa.sa)))
2969                        return -EFAULT;
2970        }
2971
2972        ret = do_sigaction(sig, act ? &new_sa : NULL, oact ? &old_sa : NULL);
2973
2974        if (!ret && oact) {
2975                if (copy_to_user(oact, &old_sa.sa, sizeof(old_sa.sa)))
2976                        return -EFAULT;
2977        }
2978out:
2979        return ret;
2980}

在我看来,不同之处在于rt_sigation复制了整个sigaction结构,而sigaction是内联获取和更改内存(使用get/set用户函数)。。。我不确定,但可能直接访问用户空间内存比使用临时副本需要更多的时间。

来自man sigaction(链接)我引用:

最初的Linux系统调用名为sigaction()。然而在Linux 2.2中添加了实时信号,该系统调用支持的32位sigset_t类型不再适用出于目的。因此,一个新的系统调用rt_sigation()被添加以支持放大的sigset_t类型。新系统调用采用第四个参数size_t-sigsettize,它指定大小以act.sa_mask和oldact.sa_amask.中的信号集的字节为单位

最新更新