线程间的调度
线程调度一句话概括:
高优先级线程抢占低优先级线程,如果没有更高优先级线程,同样优先级线程轮流执行。
线程间调度的规则:
- 抢占:高优先级先执行
- 轮转:同级轮流执行
调度的实质其实是(链表和定时器共同的工作)
启动线程代码分析:
创建线程后,需要启动线程
启动线程函数: rt_thread_startup()
这个函数最终的作用其实是把函数放入一个链表里面。
rt_thread_startup()函数在内部会调用其他函数:
rt_thread_startup(thread)
--> rt_thread_resume(thread)
--> rt_schedule_insert_thread(thread)
-->rt_list_insert_before(
&(rt_thread_priority_table[thread->current_priority]),
&(thread->tlist));
最后的函数即是实现将函数放入链表的操作。
rt_thread_priority_table,是一个全局变量,名字是优先级表,是一个链表类型的数组,
rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];
#define RT_THREAD_PRIORITY_MAX 32
这个函数就是按照0-31的优先级划分出32个,
每一个rt_thread_priority_table[ ],有一个pre指针,一个next指针
rt_list_insert_before会将线程插入到rt_thread_priority_table的pre指针下面。
函数的作用就是将函数插入到rt_thread_priority_table的before,也就是pre指针下面,也是链表next的最末端。
有了线程的列表,同一优先级的线程运行一段时间,最后切换到下一个线程,thread0 -> thread1
1、如何切换现在运行的线程
rtthread线程的启动不在这里,是在启动文件里面启动的
rtthread启动流程:
注意components.c启动的最后,调用rtthread_startup()函数,
components.c
--> rtthread_startup()
--> rt_system_scheduler_start()
--> _get_highest_priority_thread():获取最高优先级线程 highest_ready_priority
--> rt_hw_context_switch_to() 切换到最高优先级线程去运行
获取到最高优先级的线程,然后运行最高优先级的线程。
这个获取最高优先级的线程,运行最高优先级的线程,实际上就是在rt_thread_priority_table[0]往rt_thread_priority_table[31]找,找到的第一个线程就是优先级最高的线程。
2、如何表示运行一段时间
每过一个tick,系统会检查线程运行的时间,然后判断。
判断:当前线程时间是否用完?
未用完:中断返回
用完:切换线程:
1、取出线程移到rt_thread_priority_table的pre下面,next的尾部
2、取出第一个线程运行
一次取出第一个线程运行,是会从rt_thread_priority_table[0],往下找,找到第一个线程
如果有高优先级的线程,会优先运行高优先级的。
计时的函数调用关系:
startup_stm32f103xe.S
--> SysTick_Handler
--> rt_tick_increase( )
汇编文件里面,中断向量里面,有SysTick_Handler函数,系统启动的时候,会自动启动。
系统每次中断的时候,调用这个函数。
SysTick_Handler函数作用
SysTick_Handler函数中的rt_tick_increase( )会给tick增加一个计数
rt_thread_self( )取出当前线程,
--thread -> remaining_tick ,当前线程剩余tick减1
并检查记录了线程还有多少个tick可以执行,如果到0了,就调用rt_schedule()函数,
rt_schedule的作用:
1、把线程取出来,放到同优先级也就是相同rt_thread_priority_table[ ]的最末尾
2、找到最高优先级的线程来运行