SIGSTOP如何在Linux内核中运行?

时间:2015-08-11 16:12:02

标签: linux linux-kernel signals linux-device-driver

我想知道{1}}如何在Linux内核中运行。怎么处理?以及内核在处理时如何停止运行?

我熟悉内核代码库。所以,如果你可以引用很好的内核函数,事实上这就是我想要的。我不是从用户的角度来寻找高级别的描述。

我已经使用SIGSTOP语句对get_signal_to_deliver()进行了窃听(现在正在编译)。但我希望有人能够以更好的细节解释事情。

2 个答案:

答案 0 :(得分:6)

我触摸内核已经有一段时间了,但我会尝试尽可能多地提供详细信息。我不得不在其他各个地方查找一些这些东西,所以有些细节可能有点乱,但我认为这可以很好地了解幕后发生的事情。

当引发信号时,在进程描述符结构中设置TIF_SIGPENDING标志。在返回用户模式之前,内核使用test_thread_flag(TIF_SIGPENDING)测试此标志,该标志将返回true(因为信号处于挂起状态)。

发生这种情况的具体细节似乎取决于架构,但您可以see an example for um

void interrupt_end(void)
{
    if (need_resched())
        schedule();
    if (test_thread_flag(TIF_SIGPENDING))
        do_signal();
    if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME))
        tracehook_notify_resume(&current->thread.regs);
}

无论如何,它最终调用do_signal(),这也是体系结构相关的,并在相应的signal.c文件(see the example for x86)中定义:

static void do_signal(struct pt_regs *regs)
{
    struct ksignal ksig;

        if (get_signal(&ksig)) {
        /* Whee! Actually deliver the signal.  */
        handle_signal(&ksig, regs);
        return;
    }

    /* Did we come from a system call? */
    if (syscall_get_nr(current, regs) >= 0) {
        /* Restart the system call - no handlers present */
        switch (syscall_get_error(current, regs)) {
        case -ERESTARTNOHAND:
        case -ERESTARTSYS:
        case -ERESTARTNOINTR:
            regs->ax = regs->orig_ax;
            regs->ip -= 2;
            break;

        case -ERESTART_RESTARTBLOCK:
            regs->ax = NR_restart_syscall;
            regs->ip -= 2;
            break;
        }
    }

    /*
     * If there's no signal to deliver, we just put the saved sigmask
     * back.
     */
    restore_saved_sigmask();
}

如您所见,do_signal()调用了get_signal() signal.c

大部分工作都发生在get_signal()内,这是一个巨大的功能,但最终似乎在这里处理SIGSTOP的特殊情况:

if (sig_kernel_stop(signr)) {
    /*
     * The default action is to stop all threads in
     * the thread group.  The job control signals
     * do nothing in an orphaned pgrp, but SIGSTOP
     * always works.  Note that siglock needs to be
     * dropped during the call to is_orphaned_pgrp()
     * because of lock ordering with tasklist_lock.
     * This allows an intervening SIGCONT to be posted.
     * We need to check for that and bail out if necessary.
     */
    if (signr != SIGSTOP) {
        spin_unlock_irq(&sighand->siglock);

        /* signals can be posted during this window */

        if (is_current_pgrp_orphaned())
            goto relock;

        spin_lock_irq(&sighand->siglock);
    }

    if (likely(do_signal_stop(ksig->info.si_signo))) {
        /* It released the siglock.  */
        goto relock;
    }

    /*
     * We didn't actually stop, due to a race
     * with SIGCONT or something like that.
     */
    continue;
}

See the full function here

do_signal_stop()执行处理SIGSTOP的必要处理,您也可以在signal.c中找到它。它使用TASK_STOPPED将任务状态设置为__set_current_state(TASK_STOPPED)include/sched.h是在schedule()中定义的宏,用于更新当前进程描述符状态。 (see the relevant line in signal.c)。再向下,it calls schedule()kernel/sched/core.c中定义了__schedule()。它在循环中调用__schedule(),直到找到符合条件的任务。 next尝试查找要安排的下一个任务(代码中为prev),当前任务为prev。检查状态TASK_STOPPED,因为它已更改为else { deactivate_task(rq, prev, DEQUEUE_SLEEP); prev->on_rq = 0; /* * If a worker went to sleep, notify and ask workqueue * whether it wants to wake up a task to maintain * concurrency. */ ... } deactivate_task() is called,它将任务从运行队列移动到休眠队列:

deactivate_task()

nr_running通过递减rq的{​​{1}}字段并调用dequeue_task()来移除流程,从而将进程移动到新的(等待)队列。< / p>

然后,schedule()检查可运行进程的数量,并根据有效的调度策略选择进入CPU的下一个任务(我认为这有点超出了范围)。

在一天结束时,SIGSTOP将进程从可运行队列移动到等待队列,直到该进程收到SIGCONT

答案 1 :(得分:2)

几乎每次出现中断时,内核都会暂停某些进程的运行并切换到运行中断处理程序(唯一的例外是当没有进程运行时)。同样,内核将暂停运行时间过长而不放弃CPU的进程(从技术上讲,它是相同的:它只是来自定时器中断或可能是IPI)。通常在这些情况下,内核然后将挂起的进程放回运行队列,并且当调度算法确定时间正确时,它将恢复。

在SIGSTOP的情况下,发生相同的基本事情:由于接收到停止信号,受影响的进程暂停。在发送SIGCONT之前,它们不会被放回运行队列。这里没有什么特别的:SIGSTOP只是指示内核使进程不可运行,直到另行通知为止。

[注意:您似乎暗示内核停止使用SIGSTOP运行。当然不是这样。只有SIGSTOPped进程停止运行。]