FreeRTOS阅读记录-task相关

本文详细介绍了FreeRTOS操作系统中任务的创建、初始化及调度过程。重点讲解了任务切换的实现机制,包括如何通过栈和TCB来表示任务状态,以及如何在中断和服务例程中进行上下文切换。

使用FreeRTOS也很长时间了,断断续续看过,现在记录,流程写出来不难,难的是进行高度简洁的总结。

在学校时,看过UCOS-II的代码,由于版权问题,不能使用。而FreeRTOS是基于 MIT open source license,可以免费发布,可以在商业中使用,在2019年EETimes调查中,FreeRTOS是使用量排名第三的OS。

先介绍task相关的部分

一个OS要把各个task进行管理,主要就完成2件事情,一是构造好代表各task的TCB和栈,二是进行线程切换时把上一个task的上下文保存好,并切换到下一个task,只要这2部分完成的没有问题,一个最简单的RTOS就实现了,至于各task的调度策略,支持事件等复杂功能,只是锦上添花。task的上下文保存在栈上,task的需要OS维护的信息保存在TCB中,因此一个task只使用TCB和栈就可以完全表示。

一:初始化

task创建就是把表示此task的TCB和栈初始化好,等待调度。task创建好后,TCB和栈如下:

第一个是栈指针,指向一个分配的内存,第一次初始化后,内容是要切换后执行的上下文。

xStateListItem 初始化为挂在对应优先级的Task链表上,这里没画出。Event为空,表示还没有事件。

二:任务第一次开启

是由vTaskStartScheduler()完成,先创建idle线程,之后调用 xPortStartScheduler(),在其中调用vStartFirstTask(),里面执行开中断,svc 2号调用,到svc服务函数中执行。

SVC_Handler 中调用 vPortSVCHandler_C,调用vRestoreContextOfFirstTask,流程如下:

                    -> r0 = pxCurrentTCB[0];    获取当前的psp,即创建的main的线程
                    -> mpu 相关,这里暂只分析不开mpu
                    -> ldm  r0!, {r1-r2}; /* Read from stack - r1 = PSPLIM and r2 = EXC_RETURN. */
                    -> msr  psplim, r1; /* Set this task's PSPLIM value. */
                    -> movs r1, #2; msr  CONTROL, r1; /* Switch to use PSP in the thread mode. */
                    -> adds r0, #32; msr  psp, r0; isb; /* 调整栈指针,跳过R4~R11,见上图 */
                    -> mov  r0, #0; msr  basepri, r0; /* 确保终端是打开的 */
                    -> bx   r2; /* 按 EXC_RETURN 返回,之前初始化是0xfffffffd,按中断中方式返回 */

注意返回时,硬件会自动从psp地址中弹出 R0~R3, R12, LR, PC,然后此时的CPU上下文就是线程创建时的栈的内容,然后CPU开始跑第一个线程。

三:任务切换

触发任务切换只有2种情况,第一个是被动触发,当中断发生(外设或tick中断),在中断中有信号量等释放,或计时到某个时间点,需要触发之前延时等待或超时的事件;第二个是主动触发,正在执行的线程执行了延时,或释放了信号量,或获取信号量未果等,主动触发了调度。这里举一个主动调用延时的例子:

vTaskDelay(xTicksToDelay)
    -> prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
        -> uxListRemove( &( pxCurrentTCB->xStateListItem ) );  从当前链表上移除,当前链表为正执行的任务,如果不是正执行的任务,不会调用delay函数
        -> vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) );  将当前函数移到DelayedTaskList上
        -> if( xTimeToWake < xNextTaskUnblockTime ) { xNextTaskUnblockTime = xTimeToWake; }
    -> if( xAlreadyYielded == pdFALSE ) portYIELD_WITHIN_API();    置位PENDSV中断,触发并跳入 pendsv 函数。

PendSV_Handler  @ portasm.c    src\kernel\FreeRTOSv10.4.x\Source\portable\GCC\ARM_CM33_NTZ\non_secure
    -> mrs r0, psp; mrs r2, psplim; ... #保存了 r2, r3 = lr = 0xfffffffd(中断上下文),保存当前的 psp 到 TCB
    -> mov r0, configMAX_SYSCALL_INTERRUPT_PRIORITY; msr basepri, r0; dsb; isb;  调整至最高优先级,以关闭中断
    -> bl vTaskSwitchContext;    @ tasks.c    src\kernel\FreeRTOSv10.4.x\Source
        -> taskSELECT_HIGHEST_PRIORITY_TASK();
                -> while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) ) { --uxTopPriority; }  一直找到下一个最高优先级的task
                -> listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) );  使用 idle 的 xStateListItem 把 pxCurrentTCB 赋值为下一个最高优先级的task的TCB
                -> uxTopReadyPriority = uxTopPriority;
    -> mov r0, #0; msr basepri, r0;  开中断
    -> ldr r2, pxCurrentTCB; ldr r1, [r2]; ldr r0, [r1];
    -> ldmia r0!, {r2-r11}  # 取出包括 r3 = return address

    -> msr psplim, r2;  #no MPU
    -> msr psp, r0; bx r3;  执行idle task的返回地址,此时为 0xfffffffd ,即从中断返回,从psp取出上下文,开始执行下一个最高优先级的线程

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值