您的位置:首页 > 其它

调度时机分析之被动调度(之内核态抢占调度)

2015-08-05 22:25 281 查看
之前写的两个博客中讲了调度时机的用户态抢占调度部分,分别讲了系统调用返回和中断处理返回这两个点的用户态抢占的一些情况。

这篇博客会写内核抢占调度的部分。

------------------------------------------------------------------------------------------------------------------------------

内核态抢占调度

内核态抢占调度发生在下面两者情况:

◆内核进程运行过程中没有关抢占,有中断产生,在中断返回时,判断出没有关抢占就会调用preempt_schedule_irq函数

◆内核进程运行过程中又产生了更高优先级的进程。

早期的Linux内核版本不支持内核态抢占,内核进程的切换需要主动调度,这样调度延迟大,无法做到实时调度。实现可抢占内核的主要修改就是在中断处理返回时,如果是返回内核态,在通过一些检查后,就会调用preempt_schedule_irq函数。

下面从中断处理返回开始分析。在前面讲到如果中断返回时返回到内核态,就会跳转到retint_kernel执行

/* Returning to kernel space. Check if we need preemption */

/* rcx: threadinfo. interrupts off. */

.p2align

retint_kernel:

cmpl $0,threadinfo_preempt_count(%rcx) //判断是否关抢占了

jnz retint_restore_args //关抢占了,不能抢占,恢复被中断时的处理器的状态

bt $TIF_NEED_RESCHED,threadinfo_flags(%rcx) /*检测是否有设置TIF_NEED_RESCHED标志,是否需要重新调度*/

jnc retint_restore_args //不需要重新调度,恢复被中断时的处理器的状态

bt $9,EFLAGS-ARGOFFSET(%rsp)/* interrupts off? */ /*1:响应可屏蔽中断 ;0:抑制可屏蔽中断*/

jnc retint_restore_args //IF标志清零了,恢复被中断时的处理器的状态

call preempt_schedule_irq

jmp exit_intr

首先判断是否关抢占了,如果关抢占了,那么不能抢占,就跳转到retint_restore_args恢复被中断时的处理器状态。

判断是否有设置TIF_NEED_RESCHED标志,需要重新调度,如果不需要的话,也是跳转到retint_restore_args执行。

判断EFLAGS寄存器的IF标志,如果IF标志清零了,说明现在中断上下文所在的中断不是外部可屏蔽中断,也就是内部中断(或外部不可屏蔽中断),这种情况下不考虑内核态抢占的情况,直接跳转到retint_restore_args恢复被中断时的处理器状态。

上面的检查都通过之后,就会调用preempt_schedule_irq函数执行。此函数是在退出中断上下文时开抢占的情况下进程抢占调度的入口点。

asmlinkage void __sched preempt_schedule_irq(void)

{

BUG_ON(ti->preempt_count || !irqs_disabled());

need_resched:

add_preempt_count(PREEMPT_ACTIVE);

local_irq_enable();

schedule();

local_irq_disable();

sub_preempt_count(PREEMPT_ACTIVE);

barrier();

if (unlikely(test_thread_flag(TIF_NEED_RESCHED)))

goto need_resched;

}

函数首先检查是关抢占或者开中断了,如果这两种情况任何一个满足,则报错。此函数是在关中断的情况下调用的,这样是为了避免出现中断中递归的调用此函数。

接着调用add_preempt_count(PREEMPT_ACTIVE)函数设置PREEMPT_ACTIVE,这样在后面的schedule函数中见到设置了PREEMPT_ACTIVE,就不会将先前的prev进程从运行队列中移除了。这样能够防止在内核抢占时错误的将当前进程切入阻塞状态。

错误的切入阻塞状态,会有什么后果?如果一个内核进程刚刚调用set_current_state(TASK_INTERRUPTIBLE)后被中断打断,那么在中断返回时,在schedule函数里判断此进程不是TASK_RUNNING状态,如果不加上设置PREEMPT_ACTIVE,就会将此进程从运行队列中移除,不再运行,此进程后面的代码没有得到执行,今后也没有机会被唤醒执行到了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: