Skip to content

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) */

image-20240131193309840

这个是保存的寄存器的位置关系

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