本文分析调度时机中的线程睡眠和挂起。
前文参考
[1] Zephyr内核调度之调度方式与时机
[2] Zephyr线程阻塞和超时机制分析
[3] Zephyr用户模式-系统调用
[4] Zephyr内核调度之代码分析2-调度关键函数
在[1]中提到了17种调度时机,本文分析其中6种:涉及线程睡眠和挂起引起的调度
- 线程挂起
- k_thread_suspend
- 线程挂起恢复
- k_thread_resume
- 线程睡眠
- k_sleep
- k_msleep
- k_usleep
- 线程睡眠时间到
- z_thread_timeout
- 线程唤醒
- k_wakeup
- 线程主动释放CPU控制权
- k_yield
由于系统调用的关系可被应用调用的内核函数实际实现对应到sched.c中(参考[3]):
k_thread_suspend->z_impl_k_thread_suspend
k_thread_resume->z_impl_k_thread_resume
k_sleep->z_impl_k_sleep
k_usleep->z_impl_k_usleep
k_yield->z_impl_k_yield
k_wakeup->z_impl_k_wakeup
API实现分析
线程挂起
只有在挂起当前正在执行的线程时才会进行上下文切换
1 | void z_impl_k_thread_suspend(struct k_thread *thread) |
作为调度的关键函数dequeue_thread,update_cache已经在[4]中分析过。z_reschedule_unlocked后文分析
线程挂起恢复
被挂起的线程恢复时,只有被恢复的线程是最高优先级时才会进行上下文切换
1 | void z_impl_k_thread_resume(struct k_thread *thread) |
ready_thread和z_reschedule后文分析
线程睡眠
线程睡眠,是线程自己主动调用内核API让出CPU.k_msleep
直接通过k_sleep实现
1 | static inline int32_t k_msleep(int32_t ms) |
sched.c中提供2个睡眠API:
- z_impl_k_sleep
- z_impl_k_usleep
z_impl_k_usleep会将时间单位转换为tick数,通过z_tick_sleep执行sleep1
2
3
4
5
6
7
8
9int32_t z_impl_k_usleep(int us)
{
int32_t ticks;
ticks = k_us_to_ticks_ceil64(us);
ticks = z_tick_sleep(ticks);
return k_ticks_to_us_floor64(ticks);
}
z_impl_k_sleep会先判断是不是永久sleep,永久的sleep会当做挂起处理,之后再通过z_tick_sleep执行sleep
1 | int32_t z_impl_k_sleep(k_timeout_t timeout) |
sleep单只对线程有效,在isr中执行会直接assert. 只要线程执行了sleep,就会让出CPU,必然会立即引发调度
1 | static int32_t z_tick_sleep(k_ticks_t ticks) |
作为调度的关键函数z_swap已经在[4]中分析过。z_add_thread_timeout在[2]中已经分析,unready_thread后文分析
线程睡眠时间到
从[2]的分析中我们知道,线程sleep时会通过z_add_thread_timeout加入到timer中,当sleep tick到的时候会呼叫z_thread_timeout
1 | void z_thread_timeout(struct _timeout *timeout) |
ready_thread在后文分析,由于z_thread_timeout是在tickISR中执行,在ISR退出时会判断是否要进行线程的上下文切换。
线程唤醒
处于睡眠状态的线程可以被提前唤醒
1 | void z_impl_k_wakeup(k_tid_t thread) |
z_ready_thread在后文分析
线程主动释放CPU控制权
k_yield主动释放CPU控制权,如果有更高或者同优先级的线程在就绪列队中必然引起调度
1 | void z_impl_k_yield(void) |
dequeue_thread,queue_thread,z_swap均是[4]中分析过的关键函数。
内部函数分析
上一节中有提到一些和调度相关的内部函数,这里做展开分析
z_ready_thread,ready_thread,unready_thread
z_ready_thread调用的就是ready_thread,因此只用分析ready_thread和unready_thread
1 | static void ready_thread(struct k_thread *thread) |
z_reschedule,z_reschedule_unlocked和上下文切换的调用关系如下:
z_reschedule->z_swap
z_reschedule_unlocked->z_reschedule_irqlock->z_swap_irqlock