rt-thread模糊到清晰系列: thread切换相关
// 创建thread tid = rt_thread_create("main", main_thread_entry, RT_NULL, RT_MAIN_THREAD_STACK_SIZE, RT_MAIN_THREAD_PRIORITY, 20);
rt_thread_t rt_thread_create(const char *name, void (*entry)(void *parameter), void *parameter, rt_uint32_t stack_size, rt_uint8_t priority, rt_uint32_t tick) { struct rt_thread *thread; void *stack_start; thread = (struct rt_thread *)rt_object_allocate(RT_Object_Class_Thread, name); if (thread == RT_NULL) return RT_NULL; stack_start = (void *)RT_KERNEL_MALLOC(stack_size); if (stack_start == RT_NULL) { /* allocate stack failure */ rt_object_delete((rt_object_t)thread); return RT_NULL; } _rt_thread_init(thread, name, entry, parameter, stack_start, stack_size, priority, tick); return thread; }
thread的栈相关 (这是只是初始化时所指定的寄存器值,用于第一次调度是恢复给psp, 第二次以后的调度, psp会发生变化, 从而sp也会更新为和初始化时可能不一样的位置)
static rt_err_t _rt_thread_init(struct rt_thread *thread, const char *name, void (*entry)(void *parameter), void *parameter, void *stack_start, rt_uint32_t stack_size, rt_uint8_t priority, rt_uint32_t tick) { /* init thread list */ rt_list_init(&(thread->tlist)); thread->entry = (void *)entry; thread->parameter = parameter; /* stack init */ thread->stack_addr = stack_start; thread->stack_size = stack_size; /* init thread stack */ rt_memset(thread->stack_addr, '#', thread->stack_size); #ifdef ARCH_CPU_STACK_GROWS_UPWARD thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter, (void *)((char *)thread->stack_addr), (void *)rt_thread_exit); #else thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter, (rt_uint8_t *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_ubase_t)), (void *)rt_thread_exit); #endif // ... } rt_uint8_t *rt_hw_stack_init(void *tentry, void *parameter, rt_uint8_t *stack_addr, void *texit) { struct stack_frame *stack_frame; rt_uint8_t *stk; unsigned long i;
// 8字节对齐,并预留末端记录寄存器结构 stk = stack_addr + sizeof(rt_uint32_t); stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8); stk -= sizeof(struct stack_frame); stack_frame = (struct stack_frame *)stk; /* init all register */ for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++) { ((rt_uint32_t *)stack_frame)[i] = 0xdeadbeef; } stack_frame->exception_stack_frame.r0 = (unsigned long)parameter; /* r0 : argument */ stack_frame->exception_stack_frame.r1 = 0; /* r1 */ stack_frame->exception_stack_frame.r2 = 0; /* r2 */ stack_frame->exception_stack_frame.r3 = 0; /* r3 */ stack_frame->exception_stack_frame.r12 = 0; /* r12 */
// 返回接退出
stack_frame->exception_stack_frame.lr = (unsigned long)texit; /* lr */
// 入口
stack_frame->exception_stack_frame.pc = (unsigned long)tentry; /* entry point, pc */ stack_frame->exception_stack_frame.psr = 0x01000000L; /* PSR */ /* return task's current stack address */ return stk; }
切换thread
rt_hw_context_switch_interrupt EXPORT rt_hw_context_switch_interrupt rt_hw_context_switch PROC EXPORT rt_hw_context_switch ; set rt_thread_switch_interrupt_flag to 1 LDR r2, =rt_thread_switch_interrupt_flag LDR r3, [r2] CMP r3, #1 BEQ _reswitch MOV r3, #1 STR r3, [r2] LDR r2, =rt_interrupt_from_thread ; set rt_interrupt_from_thread STR r0, [r2] _reswitch LDR r2, =rt_interrupt_to_thread ; set rt_interrupt_to_thread STR r1, [r2] LDR r0, =NVIC_INT_CTRL ; trigger the PendSV exception (causes context switch) LDR r1, =NVIC_PENDSVSET STR r1, [r0] BX LR ENDP ; r0 --> switch from thread stack ; r1 --> switch to thread stack ; psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack PendSV_Handler PROC EXPORT PendSV_Handler ; disable interrupt to protect context switch MRS r2, PRIMASK CPSID I ; get rt_thread_switch_interrupt_flag LDR r0, =rt_thread_switch_interrupt_flag LDR r1, [r0] CBZ r1, pendsv_exit ; pendsv already handled ; clear rt_thread_switch_interrupt_flag to 0 MOV r1, #0x00 STR r1, [r0] LDR r0, =rt_interrupt_from_thread LDR r1, [r0] CBZ r1, switch_to_thread ; skip register save at the first time MRS r1, psp ; get from thread stack pointer STMFD r1!, {r4 - r11} ; push r4 - r11 register LDR r0, [r0] STR r1, [r0] ; update from thread stack pointer switch_to_thread LDR r1, =rt_interrupt_to_thread LDR r1, [r1] LDR r1, [r1] ; load thread stack pointer LDMFD r1!, {r4 - r11} ; pop r4 - r11 register MSR psp, r1 ; update stack pointer pendsv_exit ; restore interrupt MSR PRIMASK, r2 ORR lr, lr, #0x04 BX lr ENDP
等价伪代码
// 设置thread的切换标志, 记录切换前后的thread的sp, 产生PendSV_Handler中断
void rt_hw_context_switch_interrupt(pp_from_thread_sp, pp_to_thread_sp) { if (rt_thread_switch_interrupt_flag) { goto _reswitch; } rt_thread_switch_interrupt_flag = 1; rt_interrupt_from_thread = pp_from_thread_sp; _reswitch: rt_interrupt_to_thread = pp_to_thread_sp; [NVIC_INT_CTRL] = NVIC_PENDSVSET; }
// 1. 离开当前thread >>> 把当前{r4-r11}入栈,并把栈位置记录给thread中的sp
// 2. 进入新的thread >>> 1中的逆操作
void PendSV_Handler() { r2 = PRIMASK; CPSID I if (!rt_thread_switch_interrupt_flag) { goto pendsv_exit; } rt_thread_switch_interrupt_flag = 0; // 第一次调度,还没存在要离开的thread,因此不用压栈和记录栈顶位置给sp if (!pp_from_thread_sp) { goto switch_to_thread; } MRS r1, psp ; get from thread stack pointer
// r1! 代表 r1最终指向最后一个入栈之后 (没有叹号代表r1不变,即指向第一个入栈位置之前) STMFD r1!, {r4 - r11} ; push r4 - r11 register // stack.push(r11) stack.push(r10) ... stack.push(r4) **pp_from_thread_sp = r1 switch_to_thread: r1 = **pp_to_thread_sp LDMFD r1!, {r4 - r11} MSR psp, r1 pendsv_exit: PRIMASK, r2 }