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

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

前文参考 [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用于等待超时

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用于解除等待

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);
}