0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

时间片调度算法issue详解

RTThread物联网操作系统 来源:RTThread物联网操作系统 作者:blta 2022-07-10 13:23 次阅读

在之前 rt_schedule中need_insert_from_thread的问题提问中,笔者提出了当前时间片调度算法过于复杂,且高优先级一旦打断未执行完时间片的任务会导致该任务重新插入到其优先级readylist末尾,存在严重的不公平性(破坏了时间片的连续)。

当然笔者也PR了一个解决方案(暂未合并)

https://github.com/RT-Thread/rt-thread/pull/5954

最近又有一个小伙伴发现了时间片调度的issue

https://github.com/RT-Thread/rt-thread/issues/6092

大致的情况是:

1、低优先级的存在任务A(ticks = a),B(ticks =b),; 高优先级任务C

2、如果高优先级 C内存在延时c 正好等于A的时间片a

3、结果就是低优先级的任务只有A在一直运行, B一直运行不了

这种情况的根本原因其实还是笔者之前提到的高优先级导致当前低优先级任务插入readylist位置不对的issue,

下面笔者再次配重新整理一下这个问题,配合图例逐步分析源码并结合测试例程展示不同情况下该issue导致的问题,并尝试解决。

源码分析

rt_tick_increase


	
  1. /**

  2. * @brief This function will notify kernel there is one tick passed.

  3. * Normally, this function is invoked by clock ISR.

  4. */

  5. void rt_tick_increase(void)

  6. {

  7. struct rt_thread *thread;

  8. rt_base_t level;

  9. RT_OBJECT_HOOK_CALL(rt_tick_hook,());

  10. level = rt_hw_interrupt_disable();

  11. /* increase the global tick */

  12. #ifdef RT_USING_SMP

  13. rt_cpu_self()->tick ++;

  14. #else

  15. ++ rt_tick;

  16. #endif/* RT_USING_SMP */

  17. /* check time slice */

  18. thread = rt_thread_self();

  19. -- thread->remaining_tick;

  20. if(thread->remaining_tick ==0)

  21. {

  22. /* change to initialized tick */

  23. thread->remaining_tick = thread->init_tick;

  24. thread->stat |= RT_THREAD_STAT_YIELD;

  25. rt_hw_interrupt_enable(level);

  26. rt_schedule();

  27. }

  28. else

  29. {

  30. rt_hw_interrupt_enable(level);

  31. }

  32. /* check timer */

  33. rt_timer_check();

  34. }

里面只做了两件事:

1、当前任务的时间片递减, 如果用完了,置位RT_THREAD_STAT_YIELD状态,调用rt_schedule,2、检测是否有任务的超时了(等待资源或延时超时),如果超时,最终也会调用rt_schedule

rt_schedule

Who calling

f4ea8490-f6d0-11ec-ba43-dac502259ad0.jpg

排除componets中使用的情况,rt_schedule主要在下面情况中被使用1、clock.c : 就是刚刚提及的在Systick中断中两种比较重要的调度:时间片调度超时调度2、ipc.c ,mempool.c: 另外一种比较重要的调度:资源阻塞和就绪调度(资源调度3、scheduler.c, thread.c:本身调度器和线程API的使用导致的直接API调度4、timer.c : 软定时器超时调度,使用的也是_thread_timeout超时函数,也是超时调度 鉴于API调度一般使用在初始化阶段,Application运行中主要使用的是时间片调度,超时调度,资源调度。后面的讨论中主要绕后三种展开: f4fc739e-f6d0-11ec-ba43-dac502259ad0.jpg

源码


	
  1. /**

  2. * @brief This function will perform scheduling once. It will select one thread

  3. * with the highest priority, and switch to it immediately.

  4. */

  5. void rt_schedule(void)

  6. {

  7. rt_base_t level;

  8. struct rt_thread *to_thread;

  9. struct rt_thread *from_thread;

  10. /* disable interrupt */

  11. level = rt_hw_interrupt_disable();

  12. /* check the scheduler is enabled or not */

  13. if(rt_scheduler_lock_nest ==0)

  14. {

  15. rt_ubase_t highest_ready_priority;

  16. if(rt_thread_ready_priority_group !=0)

  17. {

  18. /* need_insert_from_thread: need to insert from_thread to ready queue */

  19. int need_insert_from_thread =0;

  20. to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority);

  21. if((rt_current_thread->stat & RT_THREAD_STAT_MASK)== RT_THREAD_RUNNING)

  22. {

  23. if(rt_current_thread->current_priority < highest_ready_priority)

  24. {

  25. to_thread = rt_current_thread;

  26. }

  27. elseif(rt_current_thread->current_priority == highest_ready_priority &&(rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK)==0)

  28. {

  29. to_thread = rt_current_thread;

  30. }

  31. else

  32. {

  33. need_insert_from_thread =1;

  34. }

  35. rt_current_thread->stat &=~RT_THREAD_STAT_YIELD_MASK;

  36. }

  37. if(to_thread != rt_current_thread)

  38. {

  39. /* if the destination thread is not the same as current thread */

  40. rt_current_priority =(rt_uint8_t)highest_ready_priority;

  41. from_thread = rt_current_thread;

  42. rt_current_thread = to_thread;

  43. RT_OBJECT_HOOK_CALL(rt_scheduler_hook,(from_thread, to_thread));

  44. if(need_insert_from_thread)

  45. {

  46. rt_schedule_insert_thread(from_thread);

  47. }

  48. rt_schedule_remove_thread(to_thread);

  49. to_thread->stat = RT_THREAD_RUNNING |(to_thread->stat &~RT_THREAD_STAT_MASK);

  50. /* switch to new thread */

  51. RT_DEBUG_LOG(RT_DEBUG_SCHEDULER,

  52. ("[%d]switch to priority#%d "

  53. "thread:%.*s(sp:0x%08x), "

  54. "from thread:%.*s(sp: 0x%08x) ",

  55. rt_interrupt_nest, highest_ready_priority,

  56. RT_NAME_MAX, to_thread->name, to_thread->sp,

  57. RT_NAME_MAX, from_thread->name, from_thread->sp));

  58. #ifdef RT_USING_OVERFLOW_CHECK

  59. _rt_scheduler_stack_check(to_thread);

  60. #endif/* RT_USING_OVERFLOW_CHECK */

  61. if(rt_interrupt_nest ==0)

  62. {

  63. externvoid rt_thread_handle_sig(rt_bool_t clean_state);

  64. RT_OBJECT_HOOK_CALL(rt_scheduler_switch_hook,(from_thread));

  65. rt_hw_context_switch((rt_ubase_t)&from_thread->sp,

  66. (rt_ubase_t)&to_thread->sp);

  67. /* enable interrupt */

  68. rt_hw_interrupt_enable(level);

  69. #ifdef RT_USING_SIGNALS

  70. /* check stat of thread for signal */

  71. level = rt_hw_interrupt_disable();

  72. if(rt_current_thread->stat & RT_THREAD_STAT_SIGNAL_PENDING)

  73. {

  74. externvoid rt_thread_handle_sig(rt_bool_t clean_state);

  75. rt_current_thread->stat &=~RT_THREAD_STAT_SIGNAL_PENDING;

  76. rt_hw_interrupt_enable(level);

  77. /* check signal status */

  78. rt_thread_handle_sig(RT_TRUE);

  79. }

  80. else

  81. {

  82. rt_hw_interrupt_enable(level);

  83. }

  84. #endif/* RT_USING_SIGNALS */

  85. goto __exit;

  86. }

  87. else

  88. {

  89. RT_DEBUG_LOG(RT_DEBUG_SCHEDULER,("switch in interrupt "));

  90. rt_hw_context_switch_interrupt((rt_ubase_t)&from_thread->sp,

  91. (rt_ubase_t)&to_thread->sp);

  92. }

  93. }

  94. else

  95. {

  96. rt_schedule_remove_thread(rt_current_thread);

  97. rt_current_thread->stat = RT_THREAD_RUNNING |(rt_current_thread->stat &~RT_THREAD_STAT_MASK);

  98. }

  99. }

  100. }

  101. /* enable interrupt */

  102. rt_hw_interrupt_enable(level);

  103. __exit:

  104. return;

  105. }

调度的过程大致如下:1、关中断2、判断调度是否上锁,rt_thread_ready_priority_group是否为03、_scheduler_get_highest_priority_thread 获取当前最高优先级4、和当前优先级再次比较,确定真实的最高优先级5、获取最高优先级readylist的第一个任务 to_thread6、to_thread 和 rt_current_thread 比较7、如果相等,简单设置状态,继续运行8、如果不相等,把当前任务插入其优先级readylist , 切换到 to_thread9、开中断

得到to_thread的再判断

f50d01a0-f6d0-11ec-ba43-dac502259ad0.jpg

之所以搞的这么复杂,是因为当前的调度策略是把运行的任务,移出了readylist,那么获取的highest_ready_priority,to_thread只是红色圈中的,还需要结合正在运行的 thread 再次判断,下面将结合下图说明上图中的1,2,3 中情况

f52013ee-f6d0-11ec-ba43-dac502259ad0.jpg

假设 当前rt_thread_priority_table中存在三个优先级列表,下标分别为h, m, l (h

1. 当前优先级小于highest_ready_priority


	
  1. if(rt_current_thread->current_priority < highest_ready_priority)

这个很好理解:没有和当前任务优先级相同的任务,其优先级readylist为空,存在如下两种情况:

1.1 低优先级就绪

存在 B,D,E3个任务, 当前 E 因资源阻塞或者延时中,B正在运行;某一时刻任务E就绪(资源就绪或者超时),插入对应readylist后发起一次调度:highest_ready_priority = l > m

f532babc-f6d0-11ec-ba43-dac502259ad0.jpg

对于某一任务的阻塞,图例仅展示资源阻塞(调度),或者延时阻塞(调度)中的一种,下同,不再说明

1.2 当前运行任务的时间片用完

同样存在 B,D,E 3个任务,当前B在运行; 某一时刻B的时间片用完,thread->stat |= RT_THREAD_STAT_YIELD,发起一次调度:highest_ready_priority = l > m因为最高优先级m下,只有B一个任务,所以尽管其时间片已经使用完,还会复位ticks,再次运行。

f532babc-f6d0-11ec-ba43-dac502259ad0.jpg

这两种情况,最后运行的还是当前任务

2. 当前优先级等于highest_ready_priority,且当前任务时间片未用完


	
  1. if(rt_current_thread->current_priority == highest_ready_priority &&(rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK)==0)

存在 B,C 两个同优先级的任务,当前C因资源阻塞或者延时中,B正在运行;某一时刻C就绪,插入对应readylist后发起一次调度:highest_ready_priority = m但此时B的时间片还未用完,依旧无需礼让切换,让C等着吧。

f54f2364-f6d0-11ec-ba43-dac502259ad0.jpg

最后运行的也当前任务

3 需要切换新的任务

可以直接列出剩余的三种情况,

3.1 当前优先级等于highest_ready_priority,且当前任务时间片用完


	
  1. if(rt_current_thread->current_priority == highest_ready_priority &&(rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK)!=0)

存在 B,C 两个同优先级的任务,当前C已经resume就绪, B正在运行;某一时刻B的时间片用完,thread->stat |= RT_THREAD_STAT_YIELD,发起一次调度:highest_ready_priority = m

f54f2364-f6d0-11ec-ba43-dac502259ad0.jpg

需要礼让,把当前任务B插入对应优先级readylist后, 然后切换到C

3.2 当前优先级大于highest_ready_priority,且当前任务时间片用完


	
  1. if(rt_current_thread->current_priority > highest_ready_priority &&(rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK)!=0)

这个情况实际是不存在的:(rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK) != 0 表示时间片用完了,是通过rt_tick_increase->rt_schedule,即时间片用完,置位RT_THREAD_STAT_YIELD后发起的一次rt_schedule调度,调度结束会清除RT_THREAD_STAT_YIELD状态rt_current_thread->current_priority > highest_ready_priority表示高优先级就绪,是通过 rt_tick_increase->rt_timer_check->_thread_timeout->rt_schedule即高优先级的超时函数触发调度一次rt_schedule调度很明显,这两种调度不会同时发生,就算rt_tick_increase->rt_schedule先发生,也会清除RT_THREAD_STAT_YIELD状态,导致条件不满足。3.3 当前优先级大于highest_ready_priority,且当前任务时间片未用完

	
  1. if(rt_current_thread->current_priority > highest_ready_priority &&(rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK)==0)

存在任务 ABCDE, 当前A,E因资源阻塞或者延时中,B正在运行;某一时刻A就绪,其任务优先级高于当前任务,虽然当前任务时间片有剩余,也必须切出

f57eb1e2-f6d0-11ec-ba43-dac502259ad0.jpg

高优先级就绪后会把当前任务插入的readylist最后,也就是这个插入导致了众多的问题出现。

f58c3d80-f6d0-11ec-ba43-dac502259ad0.jpg

rt_schedule_insert_thread


	
  1. void rt_schedule_insert_thread(struct rt_thread *thread)

  2. {

  3. registerrt_base_t temp;

  4. RT_ASSERT(thread != RT_NULL);

  5. /* disable interrupt */

  6. temp = rt_hw_interrupt_disable();

  7. /* it's current thread, it should be RUNNING thread */

  8. if(thread == rt_current_thread)

  9. {

  10. thread->stat = RT_THREAD_RUNNING |(thread->stat &~RT_THREAD_STAT_MASK);

  11. goto __exit;

  12. }

  13. /* READY thread, insert to ready queue */

  14. thread->stat = RT_THREAD_READY |(thread->stat &~RT_THREAD_STAT_MASK);

  15. /* insert thread to ready list */

  16. rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]),

  17. &(thread->tlist));

  18. RT_DEBUG_LOG(RT_DEBUG_SCHEDULER,("insert thread[%.*s], the priority: %d ",

  19. RT_NAME_MAX, thread->name, thread->current_priority));

  20. /* set priority mask */

  21. #if RT_THREAD_PRIORITY_MAX > 32

  22. rt_thread_ready_table[thread->number]|= thread->high_mask;

  23. #endif/* RT_THREAD_PRIORITY_MAX > 32 */

  24. rt_thread_ready_priority_group |= thread->number_mask;

  25. __exit:

  26. /* enable interrupt */

  27. rt_hw_interrupt_enable(temp);

  28. }

可以明显看出,rt_schedule_insert_thread调用的是rt_list_insert_before,即把当然运行任务插入到其优先级readylist的最后

问题

因为当前运行线程是remove出readylist的, 再插入readylist是必需的,但是使用rt_list_insert_before把未执行完时间片的任务插入到readylist的最后面,下次轮到该优先级时,会直接执行C,B的时间片被分成了两次或以上来执行,同理C也可能面临同样的情况。这就导致了很多问题。

f59b55ea-f6d0-11ec-ba43-dac502259ad0.jpg

测试

测试环境:基于stm32f4disc1开发板,创建3个线程分别控制LED3,LED4,LED5闪烁1、rt_led1_thread:优先级9,时间片1,延时 t1 = 5ms闪烁LED42、rt_led2_thread:优先级11,时间片t2 =4(ms),自定义200延时闪烁LED53、rt_led3_thread:优先级11,时间片t3 = 2(ms),自定义300延时闪烁LED3

创建线程


	
  1. int main(void)

  2. {

  3. rt_thread_init(&rt_led1_thread,

  4. "LED1",

  5. led1_thread_entry,

  6. RT_NULL,

  7. &rt_led1_thread_stack[0],

  8. sizeof(rt_led1_thread_stack),

  9. 6,

  10. 1);

  11. rt_thread_startup(&rt_led1_thread);

  12. rt_thread_init(&rt_led2_thread,

  13. "LED2",

  14. led2_thread_entry,

  15. RT_NULL,

  16. &rt_led2_thread_stack[0],

  17. sizeof(rt_led2_thread_stack),

  18. 11,

  19. 4);

  20. rt_thread_startup(&rt_led2_thread);

  21. rt_thread_init(&rt_led3_thread,

  22. "LED3",

  23. led3_thread_entry,

  24. RT_NULL,

  25. &rt_led3_thread_stack[0],

  26. sizeof(rt_led3_thread_stack),

  27. 11,

  28. 2);

  29. rt_thread_startup(&rt_led3_thread);

  30. while(1)

  31. {

  32. rt_thread_mdelay(1000);

  33. }

  34. return RT_EOK;

  35. }

线程入口函数


	
  1. /* customer short delay */

  2. void delay (uint32_t count)

  3. {

  4. for(; count!=0; count--);

  5. }

  6. void led1_thread_entry(void*p_arg )

  7. {

  8. for(;;)

  9. {

  10. HAL_GPIO_WritePin(GPIOD, LD4_Pin, GPIO_PIN_SET);

  11. rt_thread_delay(5);

  12. HAL_GPIO_WritePin(GPIOD, LD4_Pin, GPIO_PIN_RESET);

  13. rt_thread_delay(5);

  14. }

  15. }

  16. void led2_thread_entry(void*p_arg )

  17. {

  18. for(;;)

  19. {

  20. HAL_GPIO_WritePin(GPIOD, LD5_Pin, GPIO_PIN_SET);

  21. delay(300);

  22. HAL_GPIO_WritePin(GPIOD, LD5_Pin, GPIO_PIN_RESET);

  23. delay(300);

  24. }

  25. }

  26. void led3_thread_entry(void*p_arg )

  27. {

  28. for(;;)

  29. {

  30. HAL_GPIO_WritePin(GPIOD, LD3_Pin, GPIO_PIN_SET);

  31. delay(300);

  32. HAL_GPIO_WritePin(GPIOD, LD3_Pin, GPIO_PIN_RESET);

  33. delay(300);

  34. }

  35. }

不同时间片对调度的影响

1)t1 = 5, t2 = 4, t3=2

新就绪的任务优先级高于当前任务,当前任务时间片有剩余

通过逻辑分析仪抓取三个LED pin 电平 f5acfe26-f6d0-11ec-ba43-dac502259ad0.jpg

波形中可以明显看出thread2时间片 4ms(4 ticks) ,执行3ms后因为高优先级thread1打断,剩余的1ms会在thread3执行完后才执行。thread3甚至出现了连续执行的情况,稍后分析。总之时间片调度完全乱掉了。2)t1 = 5, t2 = 3, t3=2新就绪的任务优先级高于当前任务,当前任务时间片无剩余

f5c01b50-f6d0-11ec-ba43-dac502259ad0.jpg

还记得我们上面提到了3.2 不能同时满足的条件吗?


	
  1. if(rt_current_thread->current_priority > highest_ready_priority &&(rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK)!=0)

只是因为软件的写法(第一次rt_scheduler后清除了RT_THREAD_STAT_YIELD状态),这个条件不能同时满足。从波形图看这两条件是可以同时满足的:某一个tick中断了,线程thread2的时间片用完了,时间片调度度后, rt_timer_check发现正好有一个高优先级线程超时了,于是先后触发了两次调度:第一次:时间片调度, rt_current = thread3, rt_thread_priority_table[11] -> thread2第二次:超时调度,把rt_current插入其readylist , rt_thread_priority_table[11] -> thread2—>thread3, rt_current = thread2然后,当高优先级thread1再次阻塞时,首先运行的还是thread2。这就是为什么thread2连续运行了两个t2(3*2),同理 thread3也是, 和我们期望的轮流执行不一致。3)t1 = 5, t2 = 5, t3=x (t2/t3 =5)高优先级的延时正好等于某一低优先级线程的时间片

f5d155f0-f6d0-11ec-ba43-dac502259ad0.jpg

这种情况造成的结果比较明显,导致thread3一直没有运行的机会了,也就是小伙伴发现的issuehttps://github.com/RT-Thread/rt-thread/issues/6092从现象上看情况3是最极端,最严重的,但是你可以立即发现。不像情况1,2虽然表面上每个thread还在执行,内部的时间片已经完全乱掉,往往这种隐形的issue会导致更加严重的后果。

解决办法

既然看到了时间片调度的众多issue,也知道了问题的原因:高优先级打断未执行完时间片的任务导致该任务被重新插入到其优先级readylist末尾那么就开始着手解决。

方案一

先看下小伙伴同时提出的解决办法:https://github.com/RT-Thread/rt-thread/pull/6095/files他增加了一个RT_THREAD_STAT_SCHEDULING 状态来判断和避免issue

f5df48c2-f6d0-11ec-ba43-dac502259ad0.jpg

这种做法应该没啥问题,暂未测试

方案二

直击问题本质,既然是插入的问题导致的,调整一下插入顺序即可

f5efef4c-f6d0-11ec-ba43-dac502259ad0.jpg

除了时间片使用完,需要yield,插入其readylist的末尾,其他情况均插入readylist的头部

再次测试t1 = 5, t2 = 5, t3=2,结果如下:

f5ff5ca2-f6d0-11ec-ba43-dac502259ad0.jpg

可以看到,ticks连续执行了,线程没有重复执行,没有跳过,也没有不被执行的,完美解决。

方案三

更近一步,可不可以不把运行的thread移除readylist呢?如果可以,没有remove,也就没有了insert,没有了issue

其实FreeRTOS使用的就是这种方案:

taskSELECT_HIGHEST_PRIORITY_TASK()


	
  1. #define taskSELECT_HIGHEST_PRIORITY_TASK()

  2. {

  3. UBaseType_t uxTopPriority;

  4. /* Find the highest priority list that contains ready tasks. */

  5. portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority );

  6. configASSERT( listCURRENT_LIST_LENGTH(&( pxReadyTasksLists[ uxTopPriority ]))>0);

  7. listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB,&( pxReadyTasksLists[ uxTopPriority ]));

  8. }/* taskSELECT_HIGHEST_PRIORITY_TASK() */

listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )


	
  1. #define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )

  2. {

  3. List_t*const pxConstList =( pxList );

  4. /* Increment the index to the next item and return the item, ensuring */

  5. /* we don't return the marker used at the end of the list. */

  6. ( pxConstList )->pxIndex =( pxConstList )->pxIndex->pxNext;

  7. if((void*)( pxConstList )->pxIndex ==(void*)&(( pxConstList )->xListEnd ))

  8. {

  9. ( pxConstList )->pxIndex =( pxConstList )->pxIndex->pxNext;

  10. }

  11. ( pxTCB )=( pxConstList )->pxIndex->pvOwner;

  12. }

可以看出它再获取最高优先级后,会直接把readylist的index往后移动一次,获取下一个任务。虽然这个它的时间片ticks为常量1,但可以给我们提供一个很好的参考,没有remove, insert。笔者之前PR的一个解决方案

https://github.com/RT-Thread/rt-thread/pull/5954

f4cc5844-f6d0-11ec-ba43-dac502259ad0.gif就是基于该方案。

rt_list_jump_next

虽然rt_list_t也是一个双向链表,但是少了一个成员变量index,不能像FreeRTOS那样直接移动完成时间片的调度首先我们需要新增一个rt_list_t操作函数rt_list_jump_next,完成rt_list_t头部往后移动一次,同时要保证list成员相对顺序不变

	
  1. /**

  2. * @brief move the list to its next's next position

  3. *

  4. * @param l list to insert it

  5. */

  6. rt_inline void rt_list_jump_next(rt_list_t*l)

  7. {

  8. l->next->prev = l->prev;

  9. l->prev->next= l->next;

  10. l->prev = l->next;

  11. l->next->next->prev = l;

  12. l->next= l->next->next;

  13. l->prev->next= l;

  14. }

大致操作如下:

f62182e6-f6d0-11ec-ba43-dac502259ad0.jpg

这种直接移动list head的做法,一次调度会有6次指针赋值操作,

而原来的一次调度remove(4次), insert(4次)加起来是8次指针赋值操作

重定义_scheduler_get_highest_priority_thread


	
  1. --- a/src/scheduler.c

  2. +++ b/src/scheduler.c

  3. @@-180,6+180,19@@staticstruct rt_thread* _scheduler_get_highest_priority_thread(rt_ubase_t*high

  4. highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group)-1;

  5. #endif/* RT_THREAD_PRIORITY_MAX > 32 */

  6. +/* if current thread is yield , move the head of priority list to next and change its status to READY */

  7. +if((rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK)!=0)

  8. +{

  9. +if(rt_current_thread->tlist.next!= rt_current_thread->tlist.prev)

  10. +{

  11. +/* multiple threads, move the list head to next */

  12. + rt_list_jump_next(&rt_thread_priority_table[rt_current_thread->current_priority]);

  13. +}

  14. +

  15. +/* clear YIELD and ready thread*/

  16. + rt_current_thread->stat = RT_THREAD_READY |(rt_current_thread->stat &~(RT_THREAD_STAT_YIELD_MASK|RT_THREAD_STAT_MASK));

  17. +}

  18. +

  19. /* get highest ready priority thread */

  20. highest_priority_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next,

  21. struct rt_thread,

f63490ac-f6d0-11ec-ba43-dac502259ad0.jpg

简化rt_schedule

上面的分析可知,rt_schedule之所以搞的复杂,是因为获取的highest_ready_priority,to_thread不包含对当前正在运行thread的计算。现在我们不把rt_current_thread 移除其readylist,获得的highest_ready_priority,to_thread就是最终的

	
  1. @@-258,7+271,6@@void rt_system_scheduler_start(void)

  2. rt_current_thread = to_thread;

  3. #endif/* RT_USING_SMP */

  4. - rt_schedule_remove_thread(to_thread);

  5. to_thread->stat = RT_THREAD_RUNNING;

  6. /* switch to new thread */

  7. @@-436,28+448,8@@void rt_schedule(void)

  8. if(rt_thread_ready_priority_group !=0)

  9. {

  10. -/* need_insert_from_thread: need to insert from_thread to ready queue */

  11. -int need_insert_from_thread =0;

  12. -

  13. to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority);

  14. -if((rt_current_thread->stat & RT_THREAD_STAT_MASK)== RT_THREAD_RUNNING)

  15. -{

  16. -if(rt_current_thread->current_priority < highest_ready_priority)

  17. -{

  18. - to_thread = rt_current_thread;

  19. -}

  20. -elseif(rt_current_thread->current_priority == highest_ready_priority &&(rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK)==0)

  21. -{

  22. - to_thread = rt_current_thread;

  23. -}

  24. -else

  25. -{

  26. - need_insert_from_thread =1;

  27. -}

  28. - rt_current_thread->stat &=~RT_THREAD_STAT_YIELD_MASK;

  29. -}

  30. -

  31. if(to_thread != rt_current_thread)

  32. {

  33. /* if the destination thread is not the same as current thread */

  34. @@-467,12+459,6@@void rt_schedule(void)

  35. RT_OBJECT_HOOK_CALL(rt_scheduler_hook,(from_thread, to_thread));

  36. -if(need_insert_from_thread)

  37. -{

  38. - rt_schedule_insert_thread(from_thread);

  39. -}

  40. -

  41. - rt_schedule_remove_thread(to_thread);

  42. to_thread->stat = RT_THREAD_RUNNING |(to_thread->stat &~RT_THREAD_STAT_MASK);

  43. /* switch to new thread */

  44. @@-531,7+517,6@@void rt_schedule(void)

  45. }

  46. else

  47. {

  48. - rt_schedule_remove_thread(rt_current_thread);

  49. rt_current_thread->stat = RT_THREAD_RUNNING |(rt_current_thread->stat &~RT_THREAD_STAT_MASK);

  50. }

  51. }

  52. (END)

rt_system_scheduler_start


	
  1. @@-258,7+271,6@@void rt_system_scheduler_start(void)

  2. rt_current_thread = to_thread;

  3. #endif/* RT_USING_SMP */

  4. - rt_schedule_remove_thread(to_thread);

  5. to_thread->stat = RT_THREAD_RUNNING;

  6. /* switch to new thread */

同样测试t1 = 5, t2 = 5, t3=2,结果正常如下:

f6418f00-f6d0-11ec-ba43-dac502259ad0.jpg

审核编辑:汤梓红


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 源码
    +关注

    关注

    8

    文章

    569

    浏览量

    28506
  • 调度算法
    +关注

    关注

    1

    文章

    67

    浏览量

    11930
  • ISSUE
    +关注

    关注

    1

    文章

    5

    浏览量

    8067

原文标题:关于时间片调度算法issue的分析与解决

文章出处:【微信号:RTThread,微信公众号:RTThread物联网操作系统】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    FreeRTOS任务调度器的三种调度算法讲解(下)

    配置如下时,调度算法就会变成不带时间片的抢占式调度
    的头像 发表于 03-21 13:46 485次阅读
    FreeRTOS任务<b class='flag-5'>调度</b>器的三种<b class='flag-5'>调度</b><b class='flag-5'>算法</b>讲解(下)

    第10章 任务调度-抢占式,时间和合作式

    转rtx操作系统本章教程为大家将介绍RTX操作系统支持的任务调度方式,抢占式,时间和合作式,这部分算是RTX操作系统的核心了。对于初学者来说,要一下子就能够理解这些比较困难些,需要多花些时间
    发表于 10-04 18:11

    详解Kernel2.6调度算法

    Kernel2.6调度算法仍然是基于优先级的调度,它的算法复杂度为O(1),也就是说是调度器的开销是恒定的,与系统当前的负载没有关系。
    发表于 08-07 06:52

    为什么时间轮转调度实验中时间不起作用?

    开发板探索者STM32F407,学习UCOSIII,按照例程做到时间轮转调度实验。按照例程结果是对的,现在想看看时间没起作用是个什么情况
    发表于 11-08 04:35

    UCOS III时间轮转调度问题

    UCOS III时间轮转调度,如果时间到了程序没有运行完,那下一次进入是是否是接着上一次退出的地方继续运行如果时间
    发表于 03-09 04:36

    如何设计一种优先级和时间相结合的调度法?

    时间调度法的原理是什么?基于μC/OSII时间调度过程是怎么进行的?如何设计一种优先级和
    发表于 04-27 06:41

    STM32中基于时间的任务调度框架简介

    STM32中基于时间的任务调度框架1.前言: 由于单片机只能单线程的进行工作,只是单纯在while循环中跑程序,导致效率很低,所以采用任务调度可以实现伪多线程工作,任务
    发表于 08-24 08:19

    STM32是怎样去实现软件时间调度

    STM32 实现软件时间调度前言:在有些时候嵌入式系统不需要上RTOS的情况下,使用一个while大循环,有可能会造成一层while套一层while的情况出现.为了解决这种情况(更好的装X).这里
    发表于 08-24 07:33

    怎样利用时间轮转调度算法去实现同步时间调度的程序呢

    怎样利用时间轮转调度算法去实现同步时间调度的程序呢?
    发表于 12-20 06:16

    UCOIII时间轮转法主要有哪些应用呢

    前提:时间轮转法:主要用于分时系统中的进程调度。为了实现轮转调度,系统把所有就绪进程按先入先出的原则排成一个队列的队首进程,让它在CPU上运行一个
    发表于 02-18 06:12

    FreeRTOS时间调度概述

    一、FreeRTOS时间调度概述FreeRTOS支持多个任务同时拥有一个优先级,这些任务的调度就可以使用时间
    发表于 02-18 06:10

    分析源码并结合测试例程展示不同情况下时间调度算法issue导致的问题及解决办法

    1、对时间调度算法issue的分析在之前 rt_schedule中need_insert_from_thread的问题 提问中,笔者提出了
    发表于 06-28 17:38

    时间调度算法issue解决后续及utest测试【上】

    1、时间调度算法issue解决办法  之前针对时间
    发表于 11-24 14:47

    时间调度算法issue解决后续及utest测试【下】

    1、时间调度算法issue解决办法极端情况实例上面有惊无险,但是也提示我们考虑极端情况: 同样是刚延时,未来得及
    发表于 11-24 15:58

    动态调度算法(DSA)

    动态调度算法动态调度算法(DSA)包括2 个方面:动态调度协议(DSP)和利用非实时间隔重发控制
    发表于 03-30 10:30 1538次阅读
    动态<b class='flag-5'>调度</b><b class='flag-5'>算法</b>(DSA)