我想知道{1}}如何在Linux内核中运行。怎么处理?以及内核在处理时如何停止运行?
我熟悉内核代码库。所以,如果你可以引用很好的内核函数,事实上这就是我想要的。我不是从用户的角度来寻找高级别的描述。
我已经使用SIGSTOP
语句对get_signal_to_deliver()
进行了窃听(现在正在编译)。但我希望有人能够以更好的细节解释事情。
答案 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(¤t->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;
}
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进程停止运行。]