Appearance
RT-Thread线程管理(源代码分析02--内核)
开启
在启动代码的最后会调用一个系统时钟的启动函数
c
/**
* @ingroup SystemInit
* This function will startup scheduler. It will select one thread
* with the highest priority level, then switch to it.
*/
void rt_system_scheduler_start(void)
{
register struct rt_thread *to_thread;
rt_ubase_t highest_ready_priority;
//获取最高优先级的任务句柄
to_thread = _get_highest_priority_thread(&highest_ready_priority);
//使用全局变量记录当前任务
rt_current_thread = to_thread;
//把这个任务从ready链表里面移除, 更新全局优先级标志位
rt_schedule_remove_thread(to_thread);
to_thread->stat = RT_THREAD_RUNNING;
/* switch to new thread 切换线程, 这是一个汇编函数 */
rt_hw_context_switch_to((rt_ubase_t)&to_thread->sp);
/* never come back */
}
c
static struct rt_thread* _get_highest_priority_thread(rt_ubase_t *highest_prio)
{
register struct rt_thread *highest_priority_thread;
register rt_ubase_t highest_ready_priority;
//获取最高优先级的大小, 这一个全局变量的第几位为1表示这一个优先级有对应的任务, 优先级数值小的优先级高
highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1;
/* get highest ready priority thread 获取最高优先级的第一个任务 */
highest_priority_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next,
struct rt_thread,
tlist);
//保存一下最高的优先级大小
*highest_prio = highest_ready_priority;
return highest_priority_thread;
}
c
int __rt_ffs(int value)
{
return __builtin_ffs(value);
}
__builtin_ffs 是 gcc 内置的函数,获取一个数值:从低位起,第一个1 出现的位置,如0x11,返回的是1,0x00返回的是0,0x02,返回的是2
assembly
/*这一个函数主要是设置两个全局变量以及开启PendSV
* void rt_hw_context_switch_to(rt_uint32 to);
* R0 --> to
*/
.global rt_hw_context_switch_to
.type rt_hw_context_switch_to, %function
rt_hw_context_switch_to:
/* 记录一下要切换到的位置, 记录在rt_interrupt_to_thread里面 */
LDR R1, =rt_interrupt_to_thread
STR R0, [R1]
/* set from thread to 0 设置rt_interrupt_from_thread的值为0,表示启动第一次线程切换 */
LDR R1, =rt_interrupt_from_thread
MOV R0, #0
STR R0, [R1]
/* set interrupt flag to 1 另一个为1, 不会跳过任务切换 */
LDR R1, =rt_thread_switch_interrupt_flag
MOV R0, #1
STR R0, [R1]
/* set the PendSV exception priority 这里是设置PendSV优先级为最低, 原因可以看我的Cotex-M3部分的笔记*/
LDR R0, =SHPR3
LDR R1, =PENDSV_PRI_LOWEST
LDR.W R2, [R0,#0] /* read 读取原来的值 */
ORR R1, R1, R2 /* modify 计算新的值 */
STR R1, [R0] /* write-back 写回 */
LDR R0, =ICSR /* trigger the PendSV exception (causes context switch) 软件挂起PenSV中断 */
LDR R1, =PENDSVSET_BIT
STR R1, [R0]
/* restore MSP */
LDR r0, =SCB_VTOR
LDR r0, [r0] /* 获取中断向量表的起始地址 */
LDR r0, [r0] /* 获取中断向量表第一个的值, 这是MSP的起始地址 */
NOP
MSR msp, r0 /* 恢复MSP(清空主栈) */
/* enable interrupts at processor level */
CPSIE F
CPSIE I
/* 这时候进PendSV中断了 */
/* never reach here! */
c.equ SCB_VTOR, 0xE000ED08 /* Vector Table Offset Register */ .equ NVIC_SHPR3, 0xE000ED20 /* system priority register (3) */
这个是保存的寄存器的位置关系
assembly
/* R0 --> switch from thread stack
* R1 --> switch to thread stack
* psr, pc, LR, R12, R3, R2, R1, R0 are pushed into [from] stack
*/
.global PendSV_Handler
.type PendSV_Handler, %function
PendSV_Handler:
/* disable interrupt to protect context switch */
MRS R2, PRIMASK /* 用R2记录一下中断屏蔽等级 */
CPSID I
/* get rt_thread_switch_interrupt_flag 这一个变量会在开启时钟的时候设置为 1 */
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 清除这一个变量 */
MOVS R1, #0
STR R1, [R0]
LDR R0, =rt_interrupt_from_thread /* 第一次启动的时候没有from, 这个是0 */
LDR R1, [R0]
CBZ R1, switch_to_thread /* skip register save at the first time 第一次启动的时候不用保存寄存器 */
/* 这里是不是第一次启动的时候需要保存一下上下文 */
MRS R1, PSP /* get from thread stack pointer 更新一下PSP*/
STMFD R1!, {R4 - R11} /* push R4 - R11 register 把这几个寄存器保存起来 */
LDR R0, [R0]
STR R1, [R0] /* update from thread stack pointer 更新一下TCB里
记录PSP的一个值,用于下一次回来时候获取栈指针 */
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 更新一下PSP*/
pendsv_exit:
/* restore interrupt */
MSR PRIMASK, R2 /* 按记录的屏蔽等级恢复中断 */
ORR LR, LR, #0x04 /* EXC_RETURN为0xFFFF_FFFD返回线程模式,并使用线程堆栈(SP=PSP) */
BX LR
自动切换
在启动的时候打开了Systick的中断, 之后会周期性的进入这一个中断服务函数
c
/**
* This is the timer interrupt service routine.
*
*/
void SysTick_Handler(void)
{
/* enter interrupt 每一个中断都会调用这一个用于RT-Thread管理嵌套 */
rt_interrupt_enter();
//更新一下HAL库使用的时钟
HAL_IncTick();
//时钟的更新以及需要切换线程的话设置PendSV
rt_tick_increase();
/* leave interrupt */
rt_interrupt_leave();
}
c
/**
* This function will be invoked by BSP, when enter interrupt service routine
*
* @note please don't invoke this routine in application
*
* @see rt_interrupt_leave
*/
void rt_interrupt_enter(void)
{
rt_base_t level;
RT_DEBUG_LOG(RT_DEBUG_IRQ, ("irq coming..., irq nest:%d\n",
rt_interrupt_nest));
level = rt_hw_interrupt_disable();
rt_interrupt_nest ++; //这一个是用来记录中断嵌套的层数的
//调用一个回调函数
RT_OBJECT_HOOK_CALL(rt_interrupt_enter_hook,());
rt_hw_interrupt_enable(level);
}
c
/**
* This function will notify kernel there is one tick passed. Normally,
* this function is invoked by clock ISR.
*/
void rt_tick_increase(void)
{
struct rt_thread *thread;
/* increase the global tick 更新一下系统的时钟 */
++ rt_tick;
/* check time slice */
thread = rt_thread_self();
-- thread->remaining_tick; //更新一下线程的时钟
if (thread->remaining_tick == 0)
{
/* change to initialized tick 这一个线程没有时间了 */
thread->remaining_tick = thread->init_tick;
thread->stat |= RT_THREAD_STAT_YIELD;
/* yield 切换一个线程 */
rt_thread_yield();
}
/* check timer 进行时钟的处理, 暂时不讨论 */
rt_timer_check();
}
c
/**
* This function will let current thread yield processor, and scheduler will
* choose a highest thread to run. After yield processor, the current thread
* is still in READY state.
*
* @return RT_EOK
*/
rt_err_t rt_thread_yield(void)
{
rt_schedule();
return RT_EOK;
}
RTM_EXPORT(rt_thread_yield);
c
/**
* This function will perform one schedule. It will select one thread
* with the highest priority level, then switch to it.
*/
void rt_schedule(void)
{
rt_base_t level;
struct rt_thread *to_thread;
struct rt_thread *from_thread;
/* disable interrupt 临界区 */
level = rt_hw_interrupt_disable();
/* check the scheduler is enabled or not 看一看可不可以切换 */
if (rt_scheduler_lock_nest == 0)
{
rt_ubase_t highest_ready_priority;
if (rt_thread_ready_priority_group != 0)
{
/* need_insert_from_thread: need to insert from_thread to ready queue 记录一下当前线程需不需切换 */
int need_insert_from_thread = 0;
//获取一下最高优先级
to_thread = _get_highest_priority_thread(&highest_ready_priority);
if ((rt_current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING)
{
if (rt_current_thread->current_priority < highest_ready_priority)
{
//当前的线程优先级最高, 不需要切换
to_thread = rt_current_thread;
}
else if (rt_current_thread->current_priority == highest_ready_priority && (rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK) == 0)
{
//这一个线程有同级线程但是不希望切换
to_thread = rt_current_thread;
}
else
{
//需要切换线程
rt_current_thread->stat &= ~RT_THREAD_STAT_YIELD_MASK;
need_insert_from_thread = 1;
}
}
if (to_thread != rt_current_thread)
{
//需要进行切换
/* if the destination thread is not the same as current thread 更新一下使用的全局变量 */
rt_current_priority = (rt_uint8_t)highest_ready_priority;
from_thread = rt_current_thread;
rt_current_thread = to_thread;
//回调函数
RT_OBJECT_HOOK_CALL(rt_scheduler_hook, (from_thread, to_thread));
if (need_insert_from_thread)
{
//把当前的线程插入就绪队列
rt_schedule_insert_thread(from_thread);
}
//把下一个线程从就绪队列里面移除
rt_schedule_remove_thread(to_thread);
//更新一下状态
to_thread->stat = RT_THREAD_RUNNING | (to_thread->stat & ~RT_THREAD_STAT_MASK);
#ifdef RT_USING_OVERFLOW_CHECK
//检测一下栈有么有溢出
_rt_scheduler_stack_check(to_thread);
#endif
if (rt_interrupt_nest == 0)
{
//不是在中断中进行切换的
extern void rt_thread_handle_sig(rt_bool_t clean_state);
//切换上下文
rt_hw_context_switch((rt_ubase_t)&from_thread->sp,
(rt_ubase_t)&to_thread->sp);
/* enable interrupt */
rt_hw_interrupt_enable(level);
goto __exit;
}
else
{
RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("switch in interrupt\n"));
//切换上下文Cotex-M3里面这俩上下文切换是一个函数
rt_hw_context_switch_interrupt((rt_ubase_t)&from_thread->sp,
(rt_ubase_t)&to_thread->sp);
}
}
else
{
//不需要切换
rt_schedule_remove_thread(rt_current_thread);
rt_current_thread->stat = RT_THREAD_RUNNING | (rt_current_thread->stat & ~RT_THREAD_STAT_MASK);
}
}
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
__exit:
return;
}
c
/*这俩是一个函数
* void rt_hw_context_switch(rt_uint32 from, rt_uint32 to);
* R0 --> from
* R1 --> to
*/
.global rt_hw_context_switch_interrupt
.type rt_hw_context_switch_interrupt, %function
.global rt_hw_context_switch
.type rt_hw_context_switch, %function
rt_hw_context_switch_interrupt:
rt_hw_context_switch:
/* set rt_thread_switch_interrupt_flag to 1 设置这一个变量为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, =ICSR /* trigger the PendSV exception (causes context switch) 开启PendSV的标志位,
之后会在那里面切换 */
LDR R1, =PENDSVSET_BIT
STR R1, [R0]
BX LR