k_thread_create()函數建立一個線程,調用_impl_k_thread_create()函數,定義在zephyr-zephyr-v1.13.0\kernel\thread.c:
1 k_tid_t _impl_k_thread_create(struct k_thread *new_thread,
2 k_thread_stack_t *stack,
3 size_t stack_size, k_thread_entry_t entry,
4 void *p1, void *p2, void *p3,
5 int prio, u32_t options, s32_t delay)
6 {
7 __ASSERT(!_is_in_isr(), "Threads may not be created in ISRs");
8
9 _setup_new_thread(new_thread, stack, stack_size, entry, p1, p2, p3,
10 prio, options);
11
12 if (delay != K_FOREVER) {
13 schedule_new_thread(new_thread, delay);
14 }
15
16 return new_thread;
17 }
第9行,調用_setup_new_thread()函數。
第12行,傳進來的最後一個參數一般為K_NO_WAIT,即馬上參與排程,是以if條件成立。
第13行,調用schedule_new_thread()函數,定義在zephyr-zephyr-v1.13.0\kernel\thread.c:
1 static void schedule_new_thread(struct k_thread *thread, s32_t delay)
2 {
3 if (delay == 0) {
4 k_thread_start(thread);
5 } else {
6 s32_t ticks = _TICK_ALIGN + _ms_to_ticks(delay);
7 int key = irq_lock();
8
9 _add_thread_timeout(thread, NULL, ticks);
10 irq_unlock(key);
11 }
12 }
第3行,由于K_NO_WAIT的值就為0,是以if條件成立。
第4行,調用k_thread_start()函數,定義在zephyr-zephyr-v1.13.0\kernel\thread.c:
1 void _impl_k_thread_start(struct k_thread *thread)
2 {
3 int key = irq_lock(); /* protect kernel queues */
4
5 if (_has_thread_started(thread)) {
6 irq_unlock(key);
7 return;
8 }
9
10 _mark_thread_as_started(thread);
11 _ready_thread(thread);
12 _reschedule(key);
13 }
第5行,判斷線程的狀态是否不為_THREAD_PRESTART,如果是則直接傳回。
第10行,清除線程的_THREAD_PRESTART狀态。
第11行,前面已經分析過了。
第12行,調用_reschedule()函數,定義在zephyr-zephyr-v1.13.0\kernel\sched.c:
1 int _reschedule(int key)
2 {
3 if (_is_in_isr()) {
4 goto noswap;
5 }
6
7 if (_get_next_ready_thread() != _current) {
8 return _Swap(key);
9 }
10
11 noswap:
12 irq_unlock(key);
13 return 0;
14 }
第3行,調用_is_in_isr()函數,判斷是否處于中斷上下文,在中斷裡是不允許線程切換的,定義在arch\arm\include\kernel_arch_func.h:
#define _is_in_isr() _IsInIsr()
實際上調用的是_IsInIsr()函數,定義在zephyr-zephyr-v1.13.0\arch\arm\include\cortex_m\exc.h:
1 static ALWAYS_INLINE int _IsInIsr(void)
2 {
3 u32_t vector = __get_IPSR();
4
5 /* IRQs + PendSV (14) + SYSTICK (15) are interrupts. */
6 return (vector > 13) || (vector && !(SCB->ICSR & SCB_ICSR_RETTOBASE_Msk));
7 }
即IPSR的值(目前中斷号)大于13則認為是處于中斷上下文。
回到_reschedule()函數,第7行,調用_get_next_ready_thread()函數,定義在zephyr-zephyr-v1.13.0\kernel\include\ksched.h:
static ALWAYS_INLINE struct k_thread *_get_next_ready_thread(void)
{
return _ready_q.cache;
}
前面也說過,_ready_q.cache始終指向的是下一個要投入運作的線程。
是以,如果目前線程不是下一個要投入的線程,那麼第8行,調用_Swap()函數,定義在zephyr-zephyr-v1.13.0\kernel\include\kswap.h:
1 static inline unsigned int _Swap(unsigned int key)
2 {
3 unsigned int ret;
4 _update_time_slice_before_swap();
5
6 ret = __swap(key);
7
8 return ret;
9}
第4行,調用_update_time_slice_before_swap()函數,定義在zephyr-zephyr-v1.13.0\kernel\sched.c:
void _update_time_slice_before_swap(void)
{
/* Restart time slice count at new thread switch */
_time_slice_elapsed = 0;
}
即将時間片清0,重新開始累加。
回到_Swap()函數,第6行,調用__swap()函數,定義在zephyr-zephyr-v1.13.0\arch\arm\core\swap.c:
1 unsigned int __swap(int key)
2 {
3 /* store off key and return value */
4 _current->arch.basepri = key;
5 _current->arch.swap_return_value = _k_neg_eagain;
6
7 /* set pending bit to make sure we will take a PendSV exception */
8 SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
9
10 /* clear mask or enable all irqs to take a pendsv */
11 irq_unlock(0);
12
13 return _current->arch.swap_return_value;
14}
第4~5行,儲存key和傳回值。
第8行,置位pendsv中斷。
第11行,使能中斷,此時就會産生pendsv中斷。
下面分析pendsv中斷的處理流程,定義在zephyr-zephyr-v1.13.0\arch\arm\core\swap_helper.S:
1 SECTION_FUNC(TEXT, __pendsv)
2
3 /* protect the kernel state while we play with the thread lists */
4
5 movs.n r0, #_EXC_IRQ_DEFAULT_PRIO
6 msr BASEPRI, r0
7
8 /* load _kernel into r1 and current k_thread into r2 */
9 ldr r1, =_kernel
10 ldr r2, [r1, #_kernel_offset_to_current]
11
12 /* addr of callee-saved regs in thread in r0 */
13 ldr r0, =_thread_offset_to_callee_saved
14 add r0, r2
15
16 /* save callee-saved + psp in thread */
17 mrs ip, PSP
18
19 stmia r0, {v1-v8, ip}
20
21 /*
22 * Prepare to clear PendSV with interrupts unlocked, but
23 * don't clear it yet. PendSV must not be cleared until
24 * the new thread is context-switched in since all decisions
25 * to pend PendSV have been taken with the current kernel
26 * state and this is what we're handling currently.
27 */
28 ldr v4, =_SCS_ICSR
29 ldr v3, =_SCS_ICSR_UNPENDSV
30
31 /* _kernel is still in r1 */
32
33 /* fetch the thread to run from the ready queue cache */
34 ldr r2, [r1, _kernel_offset_to_ready_q_cache]
35
36 str r2, [r1, #_kernel_offset_to_current]
37
38 /*
39 * Clear PendSV so that if another interrupt comes in and
40 * decides, with the new kernel state baseed on the new thread
41 * being context-switched in, that it needs to reschedules, it
42 * will take, but that previously pended PendSVs do not take,
43 * since they were based on the previous kernel state and this
44 * has been handled.
45 */
46
47 /* _SCS_ICSR is still in v4 and _SCS_ICSR_UNPENDSV in v3 */
48 str v3, [v4, #0]
49
50 /* Restore previous interrupt disable state (irq_lock key) */
51 ldr r0, [r2, #_thread_offset_to_basepri]
52 movs.n r3, #0
53 str r3, [r2, #_thread_offset_to_basepri]
54
55 /* restore BASEPRI for the incoming thread */
56 msr BASEPRI, r0
57
58 /* load callee-saved + psp from thread */
59 add r0, r2, #_thread_offset_to_callee_saved
60 ldmia r0, {v1-v8, ip}
61
62 msr PSP, ip
63
64 /* exc return */
65 bx lr
CortexM進入中斷時,CPU會自動将8個寄存器(XPSR、PC、LR、R12、R3、R2、R1、R0)壓棧。
第5~6行,相當于調用irq_lock()函數。
第9~19行的作用就是将剩下的其他寄存器(R4、R5、R6、R7、R8、R9、R10、R11、PSP)也壓棧。
第28~29行,準備清pendsv中斷标志。
第34~36行,r2指向下一個要投入運作的線程,其中第36行,将_current指向要投入運作的線程。
第48行,清pendsv中斷标志。(不清也可以?)
第51~53行,清要投入運作線程的basepri變量的值。
第56行,恢複BASEPRI寄存器的值。
第59~62行,恢複R4、R5、R6、R7、R8、R9、R10、R11、PSP寄存器。
第65行,中斷傳回,自動将XPSR、PC、LR、R12、R3、R2、R1、R0寄存器彈出,即切換到下一個線程。
應用程式也可以調用k_yield()函數主動讓出CPU,定義在zephyr-zephyr-v1.13.0\kernel\sched.c:
1 void _impl_k_yield(void)
2 {
3 __ASSERT(!_is_in_isr(), "");
4
5 if (!_is_idle(_current)) {
6 LOCKED(&sched_lock) {
7 _priq_run_remove(&_kernel.ready_q.runq, _current);
8 _priq_run_add(&_kernel.ready_q.runq, _current);
9 update_cache(1);
10 }
11 }
12
13 if (_get_next_ready_thread() != _current) {
14 _Swap(irq_lock());
15 }
16 }
裡面的函數都已經分析過了,這裡不再重複。
要成功将自己切換出去(讓出CPU)的前提是有優先級比自己更高的并且已經就緒的線程。
接下來看一下線程的取消過程。應用程式調用k_thread_cancel()函數取消一個線程,定義在
zephyr-zephyr-v1.13.0\kernel\thread.c:
1 int _impl_k_thread_cancel(k_tid_t tid)
2 {
3 struct k_thread *thread = tid;
4
5 unsigned int key = irq_lock();
6
7 if (_has_thread_started(thread) ||
8 !_is_thread_timeout_active(thread)) {
9 irq_unlock(key);
10 return -EINVAL;
11 }
12
13 _abort_thread_timeout(thread);
14 _thread_monitor_exit(thread);
15
16 irq_unlock(key);
17
18 return 0;
19 }
第7~8行,如果線程都沒開始運作過,則傳回出錯。如果線程不是在等待(延時或者休眠),也傳回出錯,即線程不能自己取消自己。
第13行,調用_abort_thread_timeout()函數,定義在zephyr-zephyr-v1.13.0\kernel\include\timeout_q.h:
static inline int _abort_thread_timeout(struct k_thread *thread)
{
return _abort_timeout(&thread->base.timeout);
}
實際上調用的是_abort_timeout()函數,定義在zephyr-zephyr-v1.13.0\kernel\include\timeout_q.h:
1 static inline int _abort_timeout(struct _timeout *timeout)
2 {
3 if (timeout->delta_ticks_from_prev == _INACTIVE) {
4 return _INACTIVE;
5 }
6
7 if (!sys_dlist_is_tail(&_timeout_q, &timeout->node)) {
8 sys_dnode_t *next_node =
9 sys_dlist_peek_next(&_timeout_q, &timeout->node);
10 struct _timeout *next = (struct _timeout *)next_node;
11
12 next->delta_ticks_from_prev += timeout->delta_ticks_from_prev;
13 }
14 sys_dlist_remove(&timeout->node);
15 timeout->delta_ticks_from_prev = _INACTIVE;
16
17 return 0;
18 }
第3行,如果線程沒有在延時或者休眠,則傳回出錯。
第7行,如果線程不是在逾時隊列的最後,則if條件成立。
第9行,取出線程的下一個節點。
第12行,将下一個節點的延時時間加上要取消的線程剩餘的延時時間。
第14行,将線程從逾時隊列移除。
第15行,将線程的delta_ticks_from_prev設為_INACTIVE。