【问题标题】:What is the difference between signal and rt_signal syscalls in Linux?Linux 中的 signal 和 rt_signal 系统调用有什么区别?
【发布时间】:2012-10-05 01:01:39
【问题描述】:

我开发了一个处理 SIGILL 信号的库。因为我想避免 libc 依赖,直接使用 Linux 系统调用。我注意到我的库在某些 Linux 系统上挂起,经过大量调试后,我发现使用 rt_sigaction syscall 而不是 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 xscale\n"
        //  "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 syscall 存根函数的实现:

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;
}

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

【问题讨论】:

  • 我猜rt_sigaction 版本是“实时”版本。这意味着它被设计为具有确定性的调用时间。
  • 这些代码几乎完全相同,都位于内核中的 do_sigaction() 处。如果您遇到问题,详细说明它们可能会有所帮助。
  • 我在问题中添加了更多细节和我的代码的相关部分。
  • Marat,你的内核版本是多少?

标签: c linux linux-kernel signals system-calls


【解决方案1】:

我没有明确的答案,但我仍会努力贡献:

查看内核源码:

 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_sigaction 复制整个 sigaction 结构,而 sigaction 正在获取和更改内联内存(使用 get/set 用户函数)...我不确定,但可能需要更多是时候直接访问用户空间内存而不是使用临时副本了。

【讨论】:

    【解决方案2】:

    来自man sigaction (link) 我引用:

    最初的 Linux 系统调用名为 sigaction()。然而,随着 在 Linux 2.2 中添加了实时信号,固定大小, 该系统调用支持的 32 位 sigset_t 类型不再适合 为目的。因此,一个新的系统调用 rt_sigaction() 是 添加以支持扩大的 sigset_t 类型。新的系统调用 接受第四个参数 size_t sigsetsize,它指定大小 act.sa_mask 和 oldact.sa_mask 中信号集的字节数。

    【讨论】:

    • 同样来自 `man sigaction',如果你使用的是 glibc,那么就不需要使用 rt_sigaction。 “glibc sigaction() 包装函数对我们隐藏了这些细节,在内核提供时透明地调用 rt_sigaction()。”
    猜你喜欢
    • 1970-01-01
    • 2015-07-01
    • 2021-02-11
    • 1970-01-01
    • 2016-02-12
    • 1970-01-01
    • 2012-01-20
    • 2011-02-14
    • 1970-01-01
    相关资源
    最近更新 更多