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。