调度
初始化
- 根据支持的最大优先级,初始化不同的链表
1 |
|
- 初始化就绪优先级组
- 优先级>32, 初始化线程就绪表
位图
- 软件实现
- 硬件实现
1 |
|
插入线程
- 插入的线程时间片用完或者发生了让步(
YIELD
),证明该线程需要优先执行,将其插入到链表头部; - 否则,还是时间片未走完,将其插入到链表尾部;
3.rt_thread_ready_priority_group
置位,用于更快确认系统使用了什么优先级任务
删除线程
- 从链表中删除线程
- 判断该优先级任务链表是否为空,为空则清除
rt_thread_ready_priority_group
中的相关置位标志.
线程启动
- 线程状态设置为
RT_THREAD_SUSPEND
2.number_mask = 1L << RT_SCHED_PRIV(thread).current_priority;
系统启动
- 调度程序获取最高优先级线程
_scheduler_get_highest_priority_thread
- 注意到
rt_thread_ready_priority_group
在初始化时置0 - 线程创建启动后,将会把
rt_thread_ready_priority_group
置位,用于更快确认系统使用了什么优先级任务
例如必须创建的空闲线程,其线程优先级为``RT_THREAD_PRIORITY_MAX - 1`
- 获得最高优先级的第一个线程作为
current_thread
, 从就绪列表中删除该线程,切换线程状态为RT_THREAD_RUNNING
- 为什么要删除当前线程?
将线程从就绪队列中移除。这是因为,当线程被调度并开始运行时,它就不再处于就绪状态,因此需要从就绪队列中移除。当线程完成其任务或者被阻塞时,它会再次被添加到就绪队列中,等待下一次的调度。这种设计可以确保就绪队列中始终只包含那些实际上处于就绪状态的线程,从而提高调度的效率和准确性。
- 执行线程切换
rt_hw_context_switch_to
- 这个函数只有目标线程,没有来源线程。
- 只在第一次启动时在rt_system_scheduler_start()中调用。
- 会设置rt_interrupt_to_thread为目标线程的地址。
- 会设置rt_interrupt_from_thread为0,表示不需要保存来源线程的上下文。
- 会设置rt_thread_switch_interrupt_flag为1,表示需要进行上下文切换。
- 会设置PendSV异常的优先级为最低优先级,并触发PendSV异常。
1 |
|
临界区保护
- rt_enter_critical(void):这个函数用于锁定线程调度器。它首先禁用中断,然后将调度器锁定的层数加一,并将新的层数保存在critical_level变量中。然后,它启用中断,并返回新的调度器锁定层数。这个函数通常用于进入临界区。
- rt_exit_critical(void):这个函数用于解锁线程调度器。它首先禁用中断,然后将调度器锁定的层数减一。如果调度器锁定的层数减到0或以下,它将调度器锁定的层数重置为0,然后启用中断,并检查是否需要进行任务调度。如果调度器锁定的层数仍然大于0,它将直接启用中断。这个函数通常用于退出临界区。
- rt_critical_level(void):这个函数返回当前的调度器锁定层数。如果返回值为0,表示调度器当前未被锁定。
这些函数通常用于实现临界区,以保护共享资源的访问。在临界区内,调度器被锁定,因此不会发生上下文切换。当离开临界区时,调度器被解锁,如果有必要,还会进行重新调度。这样可以确保在临界区内的代码不会被其他线程中断,从而保护了共享资源的一致性。注意,这些函数通常在内核或驱动程序代码中使用,应用程序代码通常不直接使用它们。在应用程序代码中,通常使用互斥量、信号量等同步原语来保护共享资源。这些同步原语的实现内部可能会使用到这些函数。
调度实现
- 中断屏蔽,
rt_scheduler_lock_nest
= 0,即没有进入临界区,则进行线程调度
2.rt_thread_ready_priority_group != 0
,系统存在就绪线程,进行线程调度
- 获取最高优先级的线程和优先级
- 获取需要执行的线程
- 如果当前线程不是最高优先级线程,则进行线程切换
- 如果需要插入线程,把
from
插入就绪链表末尾 - 从就绪链表中删除
to
线程 - 执行栈异常检测
- 执行线程切换
- 不需要切换,则从就绪链表中删除
to
线程
线程切换
上下文切换
1 |
|
PENSV异常处理
1 |
|
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 wdfk-prog的个人博客!
评论