ssed in the same order they were
64 * added, time-wise.
65 */
66
67 sys_dlist_prepend(&expired, node);
68
69 timeout->delta_ticks_from_prev = _EXPIRED;
70
71 } else if (ticks <= 0) {
72 break;
73 }
74
75 irq_unlock(key);
76 key = irq_lock();
77
78 timeout = (struct _timeout *)next;
79 }
80
81 irq_unlock(key);
82
83 _handle_expired_timeouts(&expired);
84 }
代码有点多,但是原理比较简单。
第7行,初始化一个超时双向链表,用于后面存放已经超时(到期)的线程。
第11行,取出超时队列的头节点。
第17行,即如果超时队列为空(没有超时任务要处理),则直接返回。
第30行,遍历超时队列。
第41行,如果取出的线程剩余的超时时间小于ticks(这里是1),则说面线程到期了,第42行将线程的超时时间置为0。否则,第44行,将超时时间减ticks。
第47行,剩下的ticks个数,其值可能为负数。
第49行,取出下一个节点。
第51行,如果当前线程的超时时间已经到了,则if条件成立。
第54行,将当前线程从超时队列移除。
第67行,将当前线程加入到临时队列里,后面会统一处理这个队列里的线程。
第69行,将当前线程的超时时间置为_EXPIRED。
如此循环,直到ticks用完(其值小于等于0),然后跳出循环,调用83行的_handle_expired_timeouts()函数,定义在zephyr-zephyr-v1.13.0\kernel\include\timeout_q.h:
1 static inline void _handle_expired_timeouts(sys_dlist_t *expired)
2 {
3 struct _timeout *timeout;
4
5 SYS_DLIST_FOR_EACH_CONTAINER(expired, timeout, node) {
6 _handle_one_expired_timeout(timeout);
7 }
8 }
即遍历临时队列,每次调用_handle_one_expired_timeout()函数,定义在zephyr-zephyr-v1.13.0\kernel\include\timeout_q.h:
1 static inline void _handle_one_expired_timeout(struct _timeout *timeout)
2 {
3 struct k_thread *thread = timeout->thread;
4 unsigned int key = irq_lock();
5
6 timeout->delta_ticks_from_prev = _INACTIVE;
7
8 K_DEBUG("timeout %p\n", timeout);
9 if (thread) {
10 _unpend_thread_timing_out(thread, timeout);
11 _mark_thread_as_started(thread);
12 _ready_thread(thread);
13 irq_unlock(key);
14 } else {
15 irq_unlock(key);
16 if (timeout->func) {
17 timeout->func(timeout);
18 }
19 }
20 }
第6行,将超时时间置为_INACTIVE。
超时的方式有两种,一是线程调用k_sleep()等函数后将自己挂起导致的超时,二是线程调用软件定时器k_timer_start()函数导致的超时,线程本身不会挂起,只是开启了一个定时器。所以就有了第9行和第14行两种不同路径。
先看第一种方式,第10行,调用_unpend_thread_timing_out()函数,定义在zephyr-zephyr-v1.13.0\kernel\include\timeout_q.h:
1 static inline void _unpend_thread_timing_out(struct k_thread *thread,
2 struct _timeout *timeout_obj)
3 {
4 if (timeout_obj->wait_q) {
5 _unpend_thread_no_timeout(thread);
6 thread->base.timeout.wait_q = NULL;
7 }
8 }
第5行,调用_unpend_thread_no_timeout()函数,定义在zephyr-zephyr-v1.13.0\kernel\sched.c:
1 void _unpend_thread_no_timeout(struct k_thread *thread)
2 {
3 LOCKED(&sched_lock) {
4 _priq_wait_remove(&pended_on(thread)->waitq, thread);
5 _mark_thread_as_not_pending(thread);
6 }
7 }
第4行,实际上调用的是_priq_dumb_remove()函数,定义在zephyr-zephyr-v1.13.0\kernel\sched.c:
void _priq_dumb_remove(sys_dlist_t *pq, struct k_thread *thread)
{
__ASSERT_NO_MSG(!_is_idle(thread));
sys_dlist_remove(&thread->base.qnode_dlist);
}
将线程从队列移除。
回到_unpend_thread_no_timeout()函数,第5行,将线程状态设置为不是_THREAD_PENDING。
回到_handle_one_expired_timeout()函数,第11~12行这两个函数在上一篇随笔里已经分析过了。第16~17行,如果定时器超时函数不为空,则调用定时器超时函数。
至此,handle_timeouts()函数分析完了。
回到_nano_sys_clock_tick_announce()函数,第15行,调用handle_time_slicing()函数,定义在zephyr-zephyr-v1.13.0\kernel\sys_clock.c:
1 static void handle_time_slicing(s32_t ticks)
2 {
3 if (!_is_thread_time_slicing(_current)) {
4 return;
5 }
6
7 _time_slice_elapsed += tic