调度时机分析之被动调度(之内核态抢占调度)
2015-08-05 22:25
281 查看
之前写的两个博客中讲了调度时机的用户态抢占调度部分,分别讲了系统调用返回和中断处理返回这两个点的用户态抢占的一些情况。
这篇博客会写内核抢占调度的部分。
------------------------------------------------------------------------------------------------------------------------------
◆内核进程运行过程中没有关抢占,有中断产生,在中断返回时,判断出没有关抢占就会调用preempt_schedule_irq函数
◆内核进程运行过程中又产生了更高优先级的进程。
早期的Linux内核版本不支持内核态抢占,内核进程的切换需要主动调度,这样调度延迟大,无法做到实时调度。实现可抢占内核的主要修改就是在中断处理返回时,如果是返回内核态,在通过一些检查后,就会调用preempt_schedule_irq函数。
下面从中断处理返回开始分析。在前面讲到如果中断返回时返回到内核态,就会跳转到retint_kernel执行
首先判断是否关抢占了,如果关抢占了,那么不能抢占,就跳转到retint_restore_args恢复被中断时的处理器状态。
判断是否有设置TIF_NEED_RESCHED标志,需要重新调度,如果不需要的话,也是跳转到retint_restore_args执行。
判断EFLAGS寄存器的IF标志,如果IF标志清零了,说明现在中断上下文所在的中断不是外部可屏蔽中断,也就是内部中断(或外部不可屏蔽中断),这种情况下不考虑内核态抢占的情况,直接跳转到retint_restore_args恢复被中断时的处理器状态。
上面的检查都通过之后,就会调用preempt_schedule_irq函数执行。此函数是在退出中断上下文时开抢占的情况下进程抢占调度的入口点。
函数首先检查是关抢占或者开中断了,如果这两种情况任何一个满足,则报错。此函数是在关中断的情况下调用的,这样是为了避免出现中断中递归的调用此函数。
接着调用add_preempt_count(PREEMPT_ACTIVE)函数设置PREEMPT_ACTIVE,这样在后面的schedule函数中见到设置了PREEMPT_ACTIVE,就不会将先前的prev进程从运行队列中移除了。这样能够防止在内核抢占时错误的将当前进程切入阻塞状态。
错误的切入阻塞状态,会有什么后果?如果一个内核进程刚刚调用set_current_state(TASK_INTERRUPTIBLE)后被中断打断,那么在中断返回时,在schedule函数里判断此进程不是TASK_RUNNING状态,如果不加上设置PREEMPT_ACTIVE,就会将此进程从运行队列中移除,不再运行,此进程后面的代码没有得到执行,今后也没有机会被唤醒执行到了。
这篇博客会写内核抢占调度的部分。
------------------------------------------------------------------------------------------------------------------------------
内核态抢占调度
内核态抢占调度发生在下面两者情况:◆内核进程运行过程中没有关抢占,有中断产生,在中断返回时,判断出没有关抢占就会调用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 |
判断是否有设置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,就会将此进程从运行队列中移除,不再运行,此进程后面的代码没有得到执行,今后也没有机会被唤醒执行到了。
相关文章推荐
- swift2.0学习之类型方法(类方法)
- 【Android 控件使用及源码解析】 GridView规则显示图片仿微信朋友圈发图片
- Windows Server 2003,PHP连接SQL Server 2005
- 一步一步搭建apache+tomcat 负载均衡集群
- 采用fpm工具制作rpm包
- 使用 OSC Maven 第三方仓库
- chrome中iframe交互问题
- 连接不上mysqlworkbench问题解决方法
- libevent入门教程:Echo Server based on libevent - Blog of Felix021 - 日,泯然众人矣。
- [leedcode 211] Add and Search Word - Data structure design
- android学习四:xml
- Android学习_01——AutoCompleteTextView控件和MultiAutoCompleteTextView控件的应用
- 友元类
- YUV格式学习:YUV422P、YV16、NV16、NV61格式转换成RGB24
- AOP初级
- 2015暑假训练赛个人赛(8.5)
- error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
- hdu1272 小希的迷宫
- 消息队列操作函数
- error C4430: missing type specifier - int assumed. Note: C++ does not support default-int