概述
- 我现在正移植RTEMS,对其中的一个变量有疑问。_Thread_Dispatch_disable_level这个变量的默认值为1,当有两个同优先级的任务时,在进行循环调度时,并没对这个变量进行操作,只是把 _Context_Switch_necessary 这个变量置为真,但是进行调度的判断需要_Thread_Dispatch_disable_level 这个变量为0时才能进行调度。这个到底是什么实现的,还没找到其中的方法。不知道你有什么遇到这个问题。。怎么理解??
_Thread_Dispatch_disable_level 这个变量不为0时表示线程的调度被禁止。为1表示被禁止了一次,为N表示调度被嵌套禁止了(N-1)次。
_Context_Switch_necessary变量不为0表示需要进行上下文切换。
一般操作系统调度线程时,必须在以下的几种情况下发生:
1.正在运行的线程被阻塞(这种情况比较多,请参考 rtesm C user.pdf中的 task 一章);
2.线程被中断,中断执行完毕后有一个更高优先级别的任务就绪;
3.使用时间片轮转调度,当前任务的时间片用完了,有其他的同优先级别的任务就绪。
对于1和3这种情况,直接调用上下文切换的代码就可以实现线程切换。这个是由 _Context_Switch() 函数实现。不同的处理器上是不一样的。需要自己去撰写,一般都是汇编语言实现。
对于2,这种情况就相对比较复杂了。RTEMS 为了快速响应中断,并从中断中快速返回。
或者发生中断嵌套了,必须正确的知道嵌套深度,并在合适的位置进行上下文切换。
以ARM为例,/cpukit/score/cpu/arm/cpu_asm.S,中存放着进入异常的代码。
但是并未对这些情况做处理,只是简单的进入中断异常而已。
中断异常的处理代码必须自己用汇编语言撰写。
ARM的中断异常在:/c/src/lib/libbsp/arm/shared/irq/irq_asm.S中。
会看到对_ISR_Nest_level、_Thread_Dispatch_disable_level、_Context_Switch_necessary这三个变量的操作。
代码本身没有什么难度,主要理解RTEMS中断的处理流程和ARM的工作流程。您可以自己学习,我会在博客中写出这些代码的分析。
您也可以找我讨论,谢谢。
- 非常感谢您能回复我的问题,但这样的结果还不能让我满意.
首先我理解是:同优先级的多个任务,时间片轮转调度方式下,当某个任务的时间片用完后,操作系统会自动运行同优先级的其它任务.当前任务是不需要发出调度相关的信息.
当时间中断到达后,调用rtems_clock_tick--> _Thread_Tickle_timeslice --> _Thread_Reset_timeslice 在这里当前任务会被放到任务队列的末尾,最高优先级任务更改为队列中的第一个任务,上下文切换为TRUE,有必要进行任务切换.但是以上过程并没有对_Thread_Dispatch_disable_level进行操作.然后退回到rtems_clock_tick中,在这里再对是否产生调度进行判断,但判断的结果是否定的.在ARM9的中断处理代码里_Thread_Dispatch_disable_level先是增加1,然后又减少1,判断的结果依然是不会调用_Thread_Dispatch,也就不会产任务调度...........
不知道以上过程少了哪步..
抱歉,我没有将这个解释透彻。你的理解一部分是正确的,另外一部分缺少了一些细节。
同优先级的多个任务,时间片轮转调度方式下,当某个任务的时间片用完后,操作系统会自动运行同优先级的其它任务.当前任务
是不需要发出调度相关的信息.当时间中断到达后,调用rtems_clock_tick--> _Thread_Tickle_timeslice --> _Thread_Reset_timeslice 在这里当前任务会被放到任务队列的末尾,最高优先级任务更改为队列中的第一个任务,上下文切换为TRUE,有必要进行任务切换.
这部分 理解是正确的。但后面的部分就有偏差了。
首先,RTEMS来了一个tick后,操作系统就会对进行一些操作,其中包括将每个时间片减1,直到它为0。这部分代码您可以在 cpukit/rtems/src/clocktick.c中找到。在这里我们必须明白,rtems_status_code rtems_clock_tick( void )这个函数是在中断中执行的。不是用户手工调用的。
中断这块代码我不知道您仔细阅读了没有,这块有比较多的细节内容。我将
rtems 4.9.4 ARM 的 /c/src/lib/libbsp/arm/shared/irq/irq_asm.S 这部分画成活动图
(画得不好,一些和处理器高度相关的操作我省去了,你凑合着看)。
假设时间片的最后一个节拍到了,调用ExecuteITHandler后,即执行了rtems_status_code rtems_clock_tick( void )函数。您在根据这个图看看,是不是要进行调度了?这部分代码的流程比较多,但代码本身没有难度,阅读时应抓大放小。
- 看来谜底越来越近了..... 从上面的流程图里可以看到:有两种情况会产生调试.1:_ISR_Signals_to_thread_executing为真;2:_Context_Switch_necessary为真且_Thread_Dispatch_disable_level为0. _Thread_Dispath 函数里实现任务调度,在这里Thread_Dispatch_disable_level会被置为1,而且 Thread_Dispatch_disable_level的初始值也为1.就是说这个变量的默认值是1,在调试的过程中也验证了这点. 对于同优先级的多个任务,如果当前任务的时间片用完(rtems_clock_tick ->_Thread_Tickle_timeslice -> _Thread_Reset_timeslice )后会被置为一个默认值.同时Context_Switch_necessary置为真._ISR_Signals_to_thread_executing,Threa _Dispatch_disable_level变量没有变化. 结果是不满足流程里发生调度的条件....但理论上这时应该运行同优先级的其它任务
呵呵,要我怎么说呢?你说的这种情况是不存在的。系统的确是通过_Thread_Dispatch这个函数实现的。但_Thread_Dispatch_disable_level的初始值在多线程开始后,进入第一个运行的线程时清0,否则操作系统是无法调度的。
RTEMS 的操作系统比较特殊,和uC/OS-II不可同日而语。每个线程的入口不是你定义的函数,而是_Thread_Handler函数,
其中就有:
/*
* At this point, the dispatch disable level BETTER be 1.
*/
_Thread_Enable_dispatch();
这时已经将_Thread_Enable_dispatch_level改为了0,以允许调度。
注意到你说的:在调试的过程中也验证了这点. 呵呵,是用printf/printk还是单步调试? 如果使用printf/printk,
那么你的打印位置有问题;如果单步执行的话……总之,RTEMS的代码本身难度不大,主要是流程复杂,要在理解流程的前提下,看代码。不然很痛苦。
最后
以上就是纯情路灯为你收集整理的回答朋友的问题(系统调度细节)的全部内容,希望文章能够帮你解决回答朋友的问题(系统调度细节)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复