美文网首页[学习] 逆解系列
[逆解] FreeRTOS 4 - 任务调度

[逆解] FreeRTOS 4 - 任务调度

作者: TalktoEason | 来源:发表于2021-10-11 00:01 被阅读0次

    第一个任务

    FreeRTOS的vTaskStartScheduler()函数开启了第一个任务。

    除去一些初始化代码外,vTaskStartScheduler通过xPortStartScheduler调用到vPortStartFirstTask

    在ARM中,vPortStartFirstTask通常是一个汇编函数,主要目的是触发svc中断

        .align 4
    vPortStartFirstTask: .asmfunc
        ; ...
        
        ;/* Call SVC to start the first task. */
        cpsie i
        cpsie f
        dsb
        isb
        ; 注意,这里call svc,而不是PendCV
        svc #0
        .endasmfunc
    

    SVCHandler是一个简化版的PendCVHandler,它不需要保存上一个任务的上下文,直接取出任务堆栈地址,跳转。

        .align 4
    vPortSVCHandler: .asmfunc
        ;/* Get the location of the current TCB. */
        ; 取出栈地址,保存到r0
        ldr r3, pxCurrentTCBConst
        ldr r1, [r3]
        ldr r0, [r1]
        ;/* Pop the core registers. */
        ; 移除当前堆栈里面的内容
        ldmia r0!, {r4-r11}
        ; 将当前任务栈地址赋值给psp
        msr psp, r0
        isb
        ; 开中断
        mov r0, #0
        msr basepri, r0
        orr r14, #0xd
        ; 跳出中断
        bx r14
        .endasmfunc
    

    而在任务初始化的时候,FreeRTOS会以任务函数入口为基点,创建第一个挂起点信息块。以ARM CM3的代码为例,会在pxPortInitialiseStack中看到:

    *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */
    

    即将当前任务函数指针,作为pc压到栈中去。因此SVCHandler结束后,会返回到第一个任务的函数指针处。main函数此时已无法再继续了。

    任务调度

    上文中说到,vTaskSwitchContext中实现了任务调度,这个函数的主体是taskSELECT_HIGHEST_PRIORITY_TASK宏。这个宏选择当前优先级最高的可激活(ready)任务,保存进pxCurrentTCB中去。参考以下代码:

        #define taskSELECT_HIGHEST_PRIORITY_TASK()                                \
        {                                                                         \
            UBaseType_t uxTopPriority = uxTopReadyPriority;                       \
                                                                                  \
            /* Find the highest priority queue that contains ready tasks. */      \
            while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) ) \
            {                                                                     \
                configASSERT( uxTopPriority );                                    \
                --uxTopPriority;                                                  \
            }                                                                     \
                                                                                  \
            /* listGET_OWNER_OF_NEXT_ENTRY indexes through the list, so the tasks of \
             * the  same priority get an equal share of the processor time. */                    \
            listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \
            uxTopReadyPriority = uxTopPriority;                                                   \
        } /* taskSELECT_HIGHEST_PRIORITY_TASK */
    

    可见FreeRTOS的任务调度顺序不是在切换任务的时候管理的。让我们再来研究任务的激活过程。在vTaskResume的代码中可以看到:

     void vTaskResume( TaskHandle_t xTaskToResume )
     {
       // ...
             /* The ready list can be accessed even if the scheduler is
                         * suspended because this is inside a critical section. */
             // 从旧list里面移除,加入到ReadyList中。
             ( void ) uxListRemove( &( pxTCB->xStateListItem ) );
             prvAddTaskToReadyList( pxTCB );
       // ...
     }
    

    prvAddTaskToReadyList是一个宏,将任务插入到当前优先级激活任务列表的末尾。

    #define prvAddTaskToReadyList( pxTCB )                                                                 \
        traceMOVED_TASK_TO_READY_STATE( pxTCB );                                                           \
        taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority );                                                \
        listINSERT_END( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \
        tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )
    

    因此可以总结,FreeRTOS是根据优先级进行排序,先运行高优先级的激活任务。同一个优先级中,根据激活的时间顺序,先激活的任务先运行。

    相关文章

      网友评论

        本文标题:[逆解] FreeRTOS 4 - 任务调度

        本文链接:https://www.haomeiwen.com/subject/tbgkoltx.html