heb*_*bbo 4 linux signals linux-device-driver linux-kernel
我想知道SIGSTOPLinux内核是如何工作的.怎么处理?以及内核在处理时如何停止运行?
我熟悉内核代码库.所以,如果你可以引用很好的内核函数,事实上这就是我想要的.我不是从用户的角度来寻找高级描述.
我已经窃听了get_signal_to_deliver()with printk()语句(它正在编译).但我希望有人能够以更好的细节解释事情.
我触摸内核已经有一段时间了,但我会尝试尽可能多地提供详细信息.我不得不在其他各个地方查找一些这样的东西,所以有些细节可能有点乱,但我认为这可以很好地了解引擎盖下发生的事情.
当引发信号时,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(¤t->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.