Zephyr内核调度之代码分析6--同步和数据传递

Creative Commons
本作品采用知识共享署名

本文分析同步和数据传递引起的调度。

前文参考
[1] Zephyr内核调度之调度方式与时机
[2] Zephyr线程阻塞和超时机制分析
[3] Zephyr内核对象-数据传递之FIFO/LIFO
[4] Zephyr内核对象-数据传递之Stack
[5] Zephyr内核对象-数据传递之Message Queue
[6] Zephyr内核对象-数据传递之邮箱
[7] Zephyr内核对象-数据传递之管道
[8] Zephyr内核对象–同步之信号量
[9] Zephyr内核对象–同步之互斥量
[10] Zephyr内核对象–同步之轮询
[11] Zephyr内核对象–同步之条件变量

在[1]中提到了17种调度时机,本文分析其中5种,将函数调用关系简化为如下,内核对象API的具体流程可以参考[3]~[11],本文只分析和调度有关的内容

  • 等待内核对象/等待内核对象发生超时

    • z_impl_k_mutex_lock->z_pend_curr
    • z_impl_k_mutex_lock->(adjust_owner_prio->z_set_prio)&z_reschedule
    • z_impl_k_sem_take->z_pend_curr
    • z_impl_k_poll->z_pend_curr
    • z_impl_k_condvar_wait->z_pend_curr
    • z_impl_k_msgq_get->z_ready_thread&z_reschedule/z_pend_curr
    • z_impl_k_stack_pop->z_pend_curr
  • 发送内核对象

    • z_impl_k_mutex_unlock->z_unpend_first_thread&z_ready_thread->z_reschedule
    • z_impl_k_sem_give->z_ready_thread&z_reschedule
    • z_handle_obj_poll_events->signal_poll_event->signal_poller->z_unpend_thread&z_ready_thread
    • z_impl_k_poll_signal_raise->signal_poll_event->signal_triggered_work->z_abort_timeout
    • z_impl_k_condvar_signal->z_ready_thread&z_reschedule
    • z_impl_k_condvar_broadcast->z_ready_thread&z_reschedule
    • z_impl_k_msgq_put->z_ready_thread&z_reschedule/z_pend_curr
  • 放弃等待内核对象

    • z_impl_k_queue_cancel_wait->handle_poll_events->z_handle_obj_poll_events->signal_poller->z_unpend_thread&z_ready_thread
  • 清空内核对象

    • z_impl_k_msgq_purge->z_ready_thread&z_reschedule
    • z_impl_k_sem_reset->z_ready_thread&z_reschedule

Pipe和MailBox比较特殊,传输是对称的,因此既可以处于发送内核对象和可以处于等待内核对象的状态:

  • z_impl_k_pipe_put->z_pipe_put_internal->z_ready_thread&z_pend_curr
  • z_impl_k_pipe_get->z_ready_thread&z_pend_curr

  • k_mbox_put->mbox_message_put->z_unpend_thread&z_ready_thread&z_pend_curr

  • k_mbox_get->z_unpend_thread&(mbox_message_dispose->z_ready_thread&z_reschedule_unlocked)&z_pend_curr

从上面看到,引起调度的函数有z_pend_curr,z_set_prio,z_ready_thread,z_reschedule,z_unpend_thread,z_abort_timeout.
z_set_prio,z_ready_thread,z_reschedule,z_abort_timeout已经分析过。这里分析分析z_pend_curr和z_unpend_thread
z_pend_curr用于等待超时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int z_pend_curr(struct k_spinlock *lock, k_spinlock_key_t key,
_wait_q_t *wait_q, k_timeout_t timeout)
{
//pend
pend(_current, wait_q, timeout);
//进行上下文切换
return z_swap(lock, key);
}

static void pend(struct k_thread *thread, _wait_q_t *wait_q,
k_timeout_t timeout)
{
LOCKED(&sched_spinlock) {
//将当前线程加入到wait_q中
add_to_waitq_locked(thread, wait_q);
}

//将线程加入到timeout list中,超时到了后会将线程从wait_q中移除,并重新加入到read_q中
add_thread_timeout(thread, timeout);
}

static void add_to_waitq_locked(struct k_thread *thread, _wait_q_t *wait_q)
{
//将线程从ready_q中移除,并更新最优的线程
unready_thread(thread);
z_mark_thread_as_pending(thread);

SYS_PORT_TRACING_FUNC(k_thread, sched_pend, thread);

//将移除的线程加入到wait_q中
if (wait_q != NULL) {
thread->base.pended_on = wait_q;
z_priq_wait_add(&wait_q->waitq, thread);
}
}

z_unpend_thread用于解除等待

1
2
3
4
5
6
7
8
9
void z_unpend_thread(struct k_thread *thread)
{
//将线程从wait_q中移除
//z_unpend_thread_no_timeout->unpend_thread_no_timeout->_priq_wait_remove
z_unpend_thread_no_timeout(thread);

//将线程从timeout_list中移除
(void)z_abort_thread_timeout(thread);
}