Zephyr内核调度之代码分析7---时间片,Timer和调度锁
本文分析最后三种调度时间:时间片,Timer,调度锁。
前文参考
[1] Zephyr内核调度之调度方式与时机 [2] Zephyr线程阻塞和超时机制分析 [3] Zephyr内核调度之锁调度分析 [4] Zephyr内核调度之代码分析2–调度关键函数 [5] Zephyr内核对象–k_timer简介 [6] Zephyr内核调度之锁调度分析 [7] Zephyr内核调度之代码分析3–线程睡眠和挂起 [8] Zephyr内核调度之代码分析6-同步和数据传递
在[1]中提到了17种调度时机,在前面的6篇文章中分析了其中14种,本文分析剩下的三种
-
时间片到
- z_time_slicer
-
停止内核timer
- z_timer_expiration_handler->z_ready_thread
- z_impl_k_timer_status_sync->z_pend_curr
- z_impl_k_timer_stop->z_ready_thread&z_reschedule_unlocked
-
解锁调度
- k_sched_unlock->update_cache&z_reschedule_unlocked
代码分析 链接到标题
时间片到 链接到标题
每个抢占式线程都有自己的时间片,相同优先级线程之间如果时间片用完了就在Tick中断退出时进行上下文切换,在[2]中分析过在tick中断中会调用z_time_slicer进行时间片处理
void z_time_slice(int ticks)
{
k_spinlock_key_t key = k_spin_lock(&sched_spinlock);
//检查是否支持时间片
if (slice_time && sliceable(_current)) {
if (ticks >= _current_cpu->slice_ticks) {
//线程的时间片已消耗完,将线程重新加入到ready_q中,由于是在ISR中执行,上下文切换会在退出ISR时进行
move_thread_to_end_of_prio_q(_current);
z_reset_time_slice();
} else {
//时间片未到,进行时间片剩余tick数更新
_current_cpu->slice_ticks -= ticks;
}
} else {
_current_cpu->slice_ticks = 0;
}
k_spin_unlock(&sched_spinlock, key);
}
move_thread_to_end_of_prio_q将线程从ready_q中取出再加入,这样就可以排到其它同优先级线程的后面,让其它同优先级的线程得到CPU
static void move_thread_to_end_of_prio_q(struct k_thread *thread)
{
//从ready_q中取出
if (z_is_thread_queued(thread)) {
dequeue_thread(&_kernel.ready_q.runq, thread);
}
//再次加入到read_q中
queue_thread(&_kernel.ready_q.runq, thread);
//选出最合适的线程
update_cache(thread == _current);
}
dequeue_thread/queue_thread/update_cache均是关键函数,参考[4]
内核Timer 链接到标题
不只是停止内核Timer,在线程等待内核timer和内核timer到期都会引发调度,在[5]中已经对k_timer进行分析,这里简单看一下调用关系 z_timer_expiration_handler->z_ready_thread,到期时会发生调度,由于z_timer_expiration_handler是在tick中断中执行,因此上下文切换会在退出ISR时发生 z_impl_k_timer_status_sync->z_pend_curr 等待k_timer过期会发生调度 z_impl_k_timer_stop->z_ready_thread&z_reschedule_unlocked 停止k_timer会发生调度 z_ready_thread,z_reschedule_unlocked,z_pend_cur在[7]和[8]中已经分析
解锁调度 链接到标题
解锁调度在[6]中已经分析,其使用的update_cache参考[4],z_reschedule_unlocked参考[8],本文就不再做说明
关于上下文切换时机 链接到标题
了解上下文切换时机有利于在应用/驱动开发时合理安排使用内核对象,在调试调优时快速定位调度引起的问题。虽然[1]中列出的17种引发调度的时机已经全部分析完毕,但是Zephyr在不停的进化迭代,会增加或者删除一些内核对象,调整内核的流程。 相应的调度时机也会变化。因此最重要的是了解[4]中更基础的调度流程和基础函数,即便调度时机有所增加和变化最后也会走到这些流程和函数,万变不离其中。