SIGSTOP如何在Linux内核中运行?

heb*_*bbo 4 linux signals linux-device-driver linux-kernel

我想知道SIGSTOPLinux内核是如何工作的.怎么处理?以及内核在处理时如何停止运行?

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

我已经窃听了get_signal_to_deliver()with printk()语句(它正在编译).但我希望有人能够以更好的细节解释事情.

Fil*_*ves 7

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

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

发生这种情况的具体细节似乎取决于架构,但你可以看到一个例子:

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);
}
Run Code Online (Sandbox Code Playgroud)

无论如何,它最终调用do_signal(),这也是体系结构相关的,并在相应的signal.c文件中定义(请参阅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();
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的那样,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;
}
Run Code Online (Sandbox Code Playgroud)

在这里查看完整功能.

do_signal_stop()做必要的处理SIGSTOP,你也可以找到它signal.c.它将任务状态设置为TASK_STOPPEDwith __set_current_state(TASK_STOPPED),其中定义的宏include/sched.h更新当前进程描述符状态.(见相关行signal.c).进一步下来,它打电话schedule().schedule()定义于kernel/sched/core.c.它会__schedule()循环调用,直到找到符合条件的任务.__schedule()尝试找到要安排的下一个任务(next在代码中),当前任务是prev.prev检查状态,并且因为它被更改为TASK_STOPPED,deactivate_task()被调用,这将任务从运行队列移动到休眠队列:

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.
     */
     ...
}
Run Code Online (Sandbox Code Playgroud)

deactivate_task()通过递减和调用nr_running字段来从runqueue中删除进程,这会将进程移动到新的(等待)队列.rqdequeue_task()

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

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