<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          FreeRTOS系列第20篇---FreeRTOS任務(wù)創(chuàng)建分析

          共 25667字,需瀏覽 52分鐘

           ·

          2021-04-12 00:07


          關(guān)注、星標(biāo)公眾號(hào),直達(dá)精彩內(nèi)容

          ID:技術(shù)讓夢(mèng)想更偉大

          整理:李肖遙


          回顧任務(wù)的創(chuàng)建刪除

          在FreeRTOS基礎(chǔ)系列《FreeRTOS系列第10篇---FreeRTOS任務(wù)創(chuàng)建和刪除》中介紹了任務(wù)創(chuàng)建API函數(shù)xTaskCreate(),我們這里先回顧一下這個(gè)函數(shù)的聲明:

          BaseType_t xTaskCreate(
              TaskFunction_tp vTaskCode,
              const char * constpcName,
              unsigned short usStackDepth,
              void *pvParameters,
              UBaseType_t uxPriority,
              TaskHandle_t *pvCreatedTask
          );

          這個(gè)API函數(shù)的作用是創(chuàng)建新的任務(wù)并將它加入到任務(wù)就緒列表,函數(shù)參數(shù)含義為:

          • 「pvTaskCode」:函數(shù)指針,指向任務(wù)函數(shù)的入口。任務(wù)永遠(yuǎn)不會(huì)返回(位于死循環(huán)內(nèi))。該參數(shù)類(lèi)型TaskFunction_t定義在文件projdefs.h中,定義為:typedef void(*TaskFunction_t)( void * ),即參數(shù)為空指針類(lèi)型并返回空類(lèi)型。

          • 「pcName」:任務(wù)描述。主要用于調(diào)試。字符串的最大長(zhǎng)度(包括字符串結(jié)束字符)由宏configMAX_TASK_NAME_LEN指定,該宏位于FreeRTOSConfig.h文件中。

          • 「usStackDepth」:指定任務(wù)堆棧大小,能夠支持的堆棧變量數(shù)量(堆棧深度),而不是字節(jié)數(shù)。比如,在16位寬度的堆棧下,usStackDepth定義為100,則實(shí)際使用200字節(jié)堆棧存儲(chǔ)空間。堆棧的寬度乘以深度必須不超過(guò)size_t類(lèi)型所能表示的最大值。比如,size_t為16位,則可以表示堆棧的最大值是65535字節(jié)。這是因?yàn)槎褩T谏暾?qǐng)時(shí)是以字節(jié)為單位的,申請(qǐng)的字節(jié)數(shù)就是堆棧寬度乘以深度,如果這個(gè)乘積超出size_t所表示的范圍,就會(huì)溢出,分配的堆??臻g也不是我們想要的。

          • 「pvParameters」:指針,當(dāng)任務(wù)創(chuàng)建時(shí),作為一個(gè)參數(shù)傳遞給任務(wù)。

          • 「uxPriority」:任務(wù)的優(yōu)先級(jí)。具有MPU支持的系統(tǒng),可以通過(guò)置位優(yōu)先級(jí)參數(shù)的portPRIVILEGE_BIT位,隨意的在特權(quán)(系統(tǒng))模式下創(chuàng)建任務(wù)。比如,創(chuàng)建一個(gè)優(yōu)先級(jí)為2的特權(quán)任務(wù),參數(shù)uxPriority可以設(shè)置為 ( 2 | portPRIVILEGE_BIT )。

          • 「pvCreatedTask」:用于回傳一個(gè)句柄(ID),創(chuàng)建任務(wù)后可以使用這個(gè)句柄引用任務(wù)。

          雖然xTaskCreate()看上去很像函數(shù),但其實(shí)是一個(gè)宏,真正被調(diào)用的函數(shù)是xTaskGenericCreate(),xTaskCreate()宏定義如下所示:

          #define xTaskCreate( pvTaskCode, pcName, usStackDepth,pvParameters, uxPriority, pxCreatedTask )    \
                xTaskGenericCreate( ( pvTaskCode ),( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxCreatedTask), ( NULL ), ( NULL ), ( NULL ) )

          可以看到,xTaskCreatexTaskGenericCreate少了三個(gè)參數(shù),在宏定義中,這三個(gè)參數(shù)被設(shè)置為NULL。

          這三個(gè)參數(shù)用于使用靜態(tài)變量的方法分配堆棧、任務(wù)TCB空間以及設(shè)置MPU相關(guān)的參數(shù)。

          一般情況下,這三個(gè)參數(shù)是不使用的,所以任務(wù)創(chuàng)建宏xTaskCreate定義的時(shí)候,將這三個(gè)參數(shù)對(duì)用戶(hù)隱藏了。

          接下來(lái)的章節(jié)中,為了方便,我們還是稱(chēng)xTaskCreate()為函數(shù),雖然它是一個(gè)宏定義。

          上面我們提到了任務(wù)TCB(任務(wù)控制塊),這是一個(gè)需要重點(diǎn)介紹的關(guān)鍵點(diǎn)。

          它用于存儲(chǔ)任務(wù)的狀態(tài)信息,包括任務(wù)運(yùn)行時(shí)的環(huán)境。每個(gè)任務(wù)都有自己的任務(wù)TCB。

          任務(wù)TCB是一個(gè)相對(duì)比較大的數(shù)據(jù)結(jié)構(gòu),這也是情理之中的,因?yàn)榕c任務(wù)相關(guān)的代碼占到整個(gè)FreeRTOS代碼量的一半左右,這些代碼大都與任務(wù)TCB相關(guān)。

          「我們先來(lái)介紹一下任務(wù)TCB數(shù)據(jù)結(jié)構(gòu)的定義」

          typedef struct tskTaskControlBlock
          {
              volatile StackType_t    *pxTopOfStack; /*當(dāng)前堆棧的棧頂,必須位于結(jié)構(gòu)體的第一項(xiàng)*/
           
              #if ( portUSING_MPU_WRAPPERS == 1 )
                  xMPU_SETTINGS   xMPUSettings;      /*MPU設(shè)置,必須位于結(jié)構(gòu)體的第二項(xiàng)*/
              #endif
           
              ListItem_t          xStateListItem; /*任務(wù)的狀態(tài)列表項(xiàng),以引用的方式表示任務(wù)的狀態(tài)*/
              ListItem_t          xEventListItem;    /*事件列表項(xiàng),用于將任務(wù)以引用的方式掛接到事件列表*/
              UBaseType_t         uxPriority;        /*保存任務(wù)優(yōu)先級(jí),0表示最低優(yōu)先級(jí)*/
              StackType_t         *pxStack;           /*指向堆棧的起始位置*/
              char               pcTaskName[ configMAX_TASK_NAME_LEN ];/*任務(wù)名字*/
           
              #if ( portSTACK_GROWTH > 0 )
                  StackType_t     *pxEndOfStack;     /*指向堆棧的尾部*/
              #endif
           
              #if ( portCRITICAL_NESTING_IN_TCB == 1 )
                  UBaseType_t     uxCriticalNesting; /*保存臨界區(qū)嵌套深度*/
              #endif
           
              #if ( configUSE_TRACE_FACILITY == 1 )
                  UBaseType_t     uxTCBNumber;       /*保存一個(gè)數(shù)值,每個(gè)任務(wù)都有唯一的值*/
                  UBaseType_t     uxTaskNumber;      /*存儲(chǔ)一個(gè)特定數(shù)值*/
              #endif
           
              #if ( configUSE_MUTEXES == 1 )
                  UBaseType_t     uxBasePriority;    /*保存任務(wù)的基礎(chǔ)優(yōu)先級(jí)*/
                  UBaseType_t     uxMutexesHeld;
              #endif
           
              #if ( configUSE_APPLICATION_TASK_TAG == 1 )
                  TaskHookFunction_t pxTaskTag;
              #endif
           
              #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )
                  void *pvThreadLocalStoragePointers[configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
              #endif
           
              #if( configGENERATE_RUN_TIME_STATS == 1 )
                  uint32_t        ulRunTimeCounter;  /*記錄任務(wù)在運(yùn)行狀態(tài)下執(zhí)行的總時(shí)間*/
              #endif
           
              #if ( configUSE_NEWLIB_REENTRANT == 1 )
                  /* 為任務(wù)分配一個(gè)Newlibreent結(jié)構(gòu)體變量。Newlib是一個(gè)C庫(kù)函數(shù),并非FreeRTOS維護(hù),F(xiàn)reeRTOS也不對(duì)使用結(jié)果負(fù)責(zé)。如果用戶(hù)使用Newlib,必須熟知Newlib的細(xì)節(jié)*/
                  struct _reent xNewLib_reent;
              #endif
           
              #if( configUSE_TASK_NOTIFICATIONS == 1 )
                  volatile uint32_t ulNotifiedValue; /*與任務(wù)通知相關(guān)*/
                  volatile uint8_t ucNotifyState;
              #endif
           
              #if( configSUPPORT_STATIC_ALLOCATION == 1 )
                  uint8_t ucStaticAllocationFlags; /* 如果堆棧由靜態(tài)數(shù)組分配,則設(shè)置為pdTRUE,如果堆棧是動(dòng)態(tài)分配的,則設(shè)置為pdFALSE*/
              #endif
           
              #if( INCLUDE_xTaskAbortDelay == 1 )
                  uint8_t ucDelayAborted;
              #endif
           
          } tskTCB;
           
          typedef tskTCB TCB_t;

          「下面我們?cè)敿?xì)的介紹這個(gè)數(shù)據(jù)結(jié)構(gòu)的主要成員:」

          指針pxTopOfStack必須位于結(jié)構(gòu)體的第一項(xiàng),指向當(dāng)前堆棧的棧頂,對(duì)于向下增長(zhǎng)的堆棧,pxTopOfStack總是指向最后一個(gè)入棧的項(xiàng)目。

          如果使用MPU,xMPUSettings必須位于結(jié)構(gòu)體的第二項(xiàng),用于MPU設(shè)置。

          接下來(lái)是狀態(tài)列表項(xiàng)xStateListItem和事件列表項(xiàng)xEventListItem,我們?cè)谏弦徽陆榻B列表和列表項(xiàng)的文章中提到過(guò):列表被FreeRTOS調(diào)度器使用,用于跟蹤任務(wù),處于就緒、掛起、延時(shí)的任務(wù),都會(huì)被掛接到各自的列表中。

          調(diào)度器就是通過(guò)把任務(wù)TCB中的狀態(tài)列表項(xiàng)xStateListItem和事件列表項(xiàng)xEventListItem掛接到不同的列表中來(lái)實(shí)現(xiàn)上述過(guò)程的。

          在task.c中,定義了一些靜態(tài)列表變量,其中有就緒、阻塞、掛起列表,例如當(dāng)某個(gè)任務(wù)處于就緒態(tài)時(shí),調(diào)度器就將這個(gè)任務(wù)TCB的xStateListItem列表項(xiàng)掛接到就緒列表。

          事件列表項(xiàng)也與之類(lèi)似,當(dāng)隊(duì)列滿(mǎn)的情況下,任務(wù)因入隊(duì)操作而阻塞時(shí),就會(huì)將事件列表項(xiàng)掛接到隊(duì)列的等待入隊(duì)列表上。

          uxPriority用于保存任務(wù)的優(yōu)先級(jí),0為最低優(yōu)先級(jí)。任務(wù)創(chuàng)建時(shí),指定的任務(wù)優(yōu)先級(jí)就被保存到該變量中。

          指針pxStack指向堆棧的起始位置,任務(wù)創(chuàng)建時(shí)會(huì)分配指定數(shù)目的任務(wù)堆棧,申請(qǐng)堆棧內(nèi)存函數(shù)返回的指針就被賦給該變量。

          很多剛接觸FreeRTOS的人會(huì)分不清指針pxTopOfStackpxStack的區(qū)別,「這里簡(jiǎn)單說(shuō)一下:」

          pxTopOfStack指向當(dāng)前堆棧棧頂,隨著進(jìn)棧出棧,pxTopOfStack指向的位置是會(huì)變化的;

          pxStack指向當(dāng)前堆棧的起始位置,一經(jīng)分配后,堆棧起始位置就固定了,不會(huì)被改變了。

          「那么為什么需要pxStack變量呢?」

          這是因?yàn)殡S著任務(wù)的運(yùn)行,堆??赡軙?huì)溢出,在堆棧向下增長(zhǎng)的系統(tǒng)中,這個(gè)變量可用于檢查堆棧是否溢出;

          如果在堆棧向上增長(zhǎng)的系統(tǒng)中,要想確定堆棧是否溢出,還需要另外一個(gè)變量pxEndOfStack來(lái)輔助診斷是否堆棧溢出,后面會(huì)講到這個(gè)變量。

          字符數(shù)組pcTaskName用于保存任務(wù)的描述或名字,在任務(wù)創(chuàng)建時(shí),由參數(shù)指定。

          名字的長(zhǎng)度由宏configMAX_TASK_NAME_LEN(位于FreeRTOSConfig.h中)指定,包含字符串結(jié)束標(biāo)志。

          如果堆棧向上生長(zhǎng)(portSTACK_GROWTH > 0),指針pxEndOfStack指向堆棧尾部,用于檢驗(yàn)堆棧是否溢出。

          變量uxCriticalNesting用于保存臨界區(qū)嵌套深度,初始值為0。

          接下來(lái)兩個(gè)變量用于可視化追蹤,僅當(dāng)宏configUSE_TRACE_FACILITY(位于FreeRTOSConfig.h中)為1時(shí)有效。

          變量uxTCBNumber存儲(chǔ)一個(gè)數(shù)值,在創(chuàng)建任務(wù)時(shí)由內(nèi)核自動(dòng)分配數(shù)值(通常每創(chuàng)建一個(gè)任務(wù),值增加1),每個(gè)任務(wù)的uxTCBNumber值都不同,主要用于調(diào)試。

          變量uxTaskNumber用于存儲(chǔ)一個(gè)特定值,與變量uxTCBNumber不同,uxTaskNumber的數(shù)值不是由內(nèi)核分配的,而是通過(guò)API函數(shù)vTaskSetTaskNumber()來(lái)設(shè)置的,數(shù)值由函數(shù)參數(shù)指定。

          如果使用互斥量(configUSE_MUTEXES == 1),任務(wù)優(yōu)先級(jí)被臨時(shí)提高時(shí),變量uxBasePriority用來(lái)保存任務(wù)原來(lái)的優(yōu)先級(jí)。

          變量ucStaticAllocationFlags也需要說(shuō)明一下,我們前面說(shuō)過(guò)任務(wù)創(chuàng)建API函數(shù)xTaskCreate()只能使用動(dòng)態(tài)內(nèi)存分配的方式創(chuàng)建任務(wù)堆棧和任務(wù)TCB,如果要使用靜態(tài)變量實(shí)現(xiàn)任務(wù)堆棧和任務(wù)TCB就需要使用函數(shù)xTaskGenericCreate()來(lái)實(shí)現(xiàn)。

          如果任務(wù)堆?;蛉蝿?wù)TCB由靜態(tài)數(shù)組和靜態(tài)變量實(shí)現(xiàn),則將該變量設(shè)置為pdTRUE(任務(wù)堆??臻g由靜態(tài)數(shù)組變量實(shí)現(xiàn)時(shí)為0x01,任務(wù)TCB由靜態(tài)變量實(shí)現(xiàn)時(shí)為0x02,任務(wù)堆棧和任務(wù)TCB都由靜態(tài)變量實(shí)現(xiàn)時(shí)為0x03),如果堆棧是動(dòng)態(tài)分配的,則將該變量設(shè)置為pdFALSE。

          到這里任務(wù)TCB的數(shù)據(jù)結(jié)構(gòu)就講完了,下面我們用一個(gè)例子「來(lái)講述任務(wù)創(chuàng)建的過(guò)程」,為方便起見(jiàn),假設(shè)被創(chuàng)建的任務(wù)叫“任務(wù)A”,任務(wù)函數(shù)為vTask_A():

          TaskHandle_t xHandle;
          xTaskCreate(vTask_A,”Task A”,120,NULL,1,&xHandle);

          這里創(chuàng)建了一個(gè)任務(wù),任務(wù)優(yōu)先級(jí)為1,由于硬件平臺(tái)是32為架構(gòu),所以指定了120*4=480字節(jié)的任務(wù)堆棧,向任務(wù)函數(shù)vTask_A()傳遞的參數(shù)為空(NULL),任務(wù)句柄由變量xHandle保存。

          當(dāng)這個(gè)語(yǔ)句執(zhí)行后,任務(wù)A被創(chuàng)建并加入就緒任務(wù)列表,我們這章的主要目的,就是看看這個(gè)語(yǔ)句在執(zhí)行過(guò)程中,發(fā)生了什么事情。

          1.創(chuàng)建任務(wù)堆棧和任務(wù)TCB

          調(diào)用函數(shù)prvAllocateTCBAndStack()創(chuàng)建任務(wù)堆棧和任務(wù)TCB。

          「有兩種方式創(chuàng)建任務(wù)堆棧和任務(wù)TCB:」

          一種是使用動(dòng)態(tài)內(nèi)存分配方法,這樣當(dāng)任務(wù)刪除時(shí),任務(wù)堆棧和任務(wù)控制塊空間會(huì)被釋放,可用于其它任務(wù);

          另一種是使用靜態(tài)變量來(lái)實(shí)現(xiàn),在創(chuàng)建任務(wù)前定義好全局或者靜態(tài)堆棧數(shù)組和任務(wù)控制塊變量,在調(diào)用創(chuàng)建任務(wù)API函數(shù)時(shí),將這兩個(gè)變量以參數(shù)的形式傳遞給任務(wù)創(chuàng)建函數(shù)xTaskGenericCreate()。

          如果使用默認(rèn)的xTaskCreate()創(chuàng)建任務(wù)函數(shù),則使用動(dòng)態(tài)內(nèi)存分配,因?yàn)榕c靜態(tài)內(nèi)存分配有關(guān)的參數(shù)不可見(jiàn)(在本文一開(kāi)始我們說(shuō)過(guò)xTaskCreate()其實(shí)是一個(gè)帶參數(shù)的宏定義,真正被執(zhí)行的函數(shù)是xTaskGenericCreate()。

          參考宏xTaskCreate()的定義可以知道,xTaskCreate()對(duì)外隱藏了使用靜態(tài)內(nèi)存分配的參數(shù),在調(diào)用xTaskGenericCreate()時(shí),這些參數(shù)被設(shè)置為NULL。

          任務(wù)堆棧成功分配后,經(jīng)過(guò)對(duì)齊的堆棧起始地址被保存到任務(wù)TCB的pxStack字段。

          如果使能堆棧溢出檢查或者使用可視化追蹤功能,則使用固定值tskSTACK_FILL_BYTE(0xa5)填充堆棧。

          函數(shù)prvAllocateTCBAndStack()的源碼去除斷言和不常用的條件編譯后如下所示:

          static TCB_t *prvAllocateTCBAndStack( const uint16_t usStackDepth, StackType_t * const puxStackBuffer, TCB_t * const pxTaskBuffer )
          {
          TCB_t *pxNewTCB;
          StackType_t *pxStack;

            /* 分配堆??臻g*/
            pxStack = ( StackType_t * ) pvPortMallocAligned( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ), puxStackBuffer );
            if( pxStack != NULL )
            {
                /* 分配TCB空間 */
                pxNewTCB = ( TCB_t * ) pvPortMallocAligned( sizeof( TCB_t ), pxTaskBuffer );

                if( pxNewTCB != NULL )
                {
                    /* 將堆棧起始位置存入TCB*/
                    pxNewTCB->pxStack = pxStack;
                }
                else
                {
                    /* 如果TCB分配失敗,釋放之前申請(qǐng)的堆棧空間 */
                    if( puxStackBuffer == NULL )
                    {
                        vPortFree( pxStack );
                    }
                }
            }
            else
            {
                pxNewTCB = NULL;
            }

            if( pxNewTCB != NULL )
            {
                /* 如果需要,使用固定值填充堆棧 */
                #if( ( configCHECK_FOR_STACK_OVERFLOW> 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark== 1 ) )
                {
                    /* 僅用于調(diào)試 */
                    ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) usStackDepth * sizeof( StackType_t ) );
                }
                #endif
            }

            return pxNewTCB;
          }

          2.初始化任務(wù)TCB必要的字段

          調(diào)用函數(shù)prvInitialiseTCBVariables()初始化任務(wù)TCB必要的字段。

          在調(diào)用創(chuàng)建任務(wù)API函數(shù)xTaskCreate()時(shí),參數(shù)pcName(任務(wù)描述)、uxPriority(任務(wù)優(yōu)先級(jí))都會(huì)被寫(xiě)入任務(wù)TCB相應(yīng)的字段,TCB字段中的xStateListItemxEventListItem列表項(xiàng)也會(huì)被初始化,初始化后的列表項(xiàng)如圖2-1所示。

          在圖2-1中,列表項(xiàng)xEventListItem的成員列表項(xiàng)值xItemValue被初始為4,這是因?yàn)槲以趹?yīng)用中設(shè)置的最大優(yōu)先級(jí)數(shù)目configMAX_PRIORITIES為5,而xEventListItem.xItemValue等于configMAX_PRIORITIES減去任務(wù)A的優(yōu)先級(jí)(為1),即5-1=4。

          這一點(diǎn)很重要,在這里xItemValue不是直接保存任務(wù)優(yōu)先級(jí),而是保存優(yōu)先級(jí)的補(bǔ)數(shù),這意味著xItemValue的值越大,對(duì)應(yīng)的任務(wù)優(yōu)先級(jí)越小。

          FreeRTOS內(nèi)核使用vListInsert函數(shù)(詳細(xì)見(jiàn)高級(jí)篇第一章)將事件列表項(xiàng)插入到一個(gè)列表,這個(gè)函數(shù)根據(jù)xItemValue的值的大小順序來(lái)進(jìn)行插入操作。

          使用宏listGET_OWNER_OF_HEAD_ENTRY獲得列表中的第一個(gè)列表項(xiàng)的xTiemValue值總是最小,也就是優(yōu)先級(jí)最高的任務(wù)!

          圖2-1:初始化狀態(tài)和事件列表項(xiàng)

          此外,TCB其它的一些字段也被初始化,比如臨界區(qū)嵌套次數(shù)、運(yùn)行時(shí)間計(jì)數(shù)器、任務(wù)通知值、任務(wù)通知狀態(tài)等。

          函數(shù)prvInitialiseTCBVariables()的源碼如下所示:

          static void prvInitialiseTCBVariables( TCB_t * const pxTCB, const char * const pcName, UBaseType_t uxPriority,   \
          const MemoryRegion_t * const xRegions, const uint16_t usStackDepth )
          {
          UBaseType_t x;

            /* 將任務(wù)描述存入TCB */
            for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
            {
                pxTCB->pcTaskName[ x ] = pcName[ x ];
                if( pcName[ x ] == 0x00 )
                {
                    break;
                }
            }
            /* 確保字符串有結(jié)束 */
            pxTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';

            /* 調(diào)整優(yōu)先級(jí),宏configMAX_PRIORITIES的值在FreeRTOSConfig.h中設(shè)置 */
            if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
            {
                uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
            }

            pxTCB->uxPriority = uxPriority;
            #if ( configUSE_MUTEXES == 1 )              /*使用互斥量*/
            {  
                pxTCB->uxBasePriority = uxPriority;
                pxTCB->uxMutexesHeld = 0;
            }
            #endif /* configUSE_MUTEXES */

            /*初始化列表項(xiàng)*/
            vListInitialiseItem( &( pxTCB->xStateListItem ) );
            vListInitialiseItem( &( pxTCB->xEventListItem ) );

            /* 設(shè)置列表項(xiàng)xStateListItem的成員pvOwner指向當(dāng)前任務(wù)控制塊 */
            listSET_LIST_ITEM_OWNER( &( pxTCB->xStateListItem ), pxTCB );

            /* 設(shè)置列表項(xiàng)xEventListItem的成員xItemValue*/
            listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );
            /* 設(shè)置列表項(xiàng)xEventListItem的成員pvOwner指向當(dāng)前任務(wù)控制塊 */
            listSET_LIST_ITEM_OWNER( &( pxTCB->xEventListItem ), pxTCB );

            #if ( portCRITICAL_NESTING_IN_TCB ==1 )    /*使能臨界區(qū)嵌套功能*/
            {  
                pxTCB->uxCriticalNesting = ( UBaseType_t ) 0U;
            }
            #endif /* portCRITICAL_NESTING_IN_TCB */

            #if ( configUSE_APPLICATION_TASK_TAG == 1 ) /*使能任務(wù)標(biāo)簽功能*/
            {  
                pxTCB->pxTaskTag = NULL;
            }
            #endif /* configUSE_APPLICATION_TASK_TAG */

            #if ( configGENERATE_RUN_TIME_STATS == 1 )  /*使能事件統(tǒng)計(jì)功能*/
            {
                pxTCB->ulRunTimeCounter = 0UL;
            }
            #endif /* configGENERATE_RUN_TIME_STATS */

            #if ( portUSING_MPU_WRAPPERS == 1 )         /*使用MPU功能*/
            {
                vPortStoreTaskMPUSettings( &( pxTCB->xMPUSettings ), xRegions, pxTCB->pxStack, usStackDepth );
            }
            #else /* portUSING_MPU_WRAPPERS */
            {
                ( void ) xRegions;
                ( void ) usStackDepth;
            }
            #endif /* portUSING_MPU_WRAPPERS */

            #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 )/*使能線(xiàn)程本地存儲(chǔ)指針*/
            {
                for( x = 0; x < ( UBaseType_t )configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ )
                {
                    pxTCB->pvThreadLocalStoragePointers[ x ] = NULL;
                }
            }
            #endif

            #if ( configUSE_TASK_NOTIFICATIONS == 1 )   /*使能任務(wù)通知功能*/
            {
                pxTCB->ulNotifiedValue = 0;
                pxTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
            }
            #endif

            #if ( configUSE_NEWLIB_REENTRANT == 1 )     /*使用Newlib*/
            {
                _REENT_INIT_PTR( ( &( pxTCB->xNewLib_reent ) ) );
            }
            #endif

            #if( INCLUDE_xTaskAbortDelay == 1 )
            {
                pxTCB->ucDelayAborted = pdFALSE;
            }
            #endif
          }

          3.初始化任務(wù)堆棧

          調(diào)用函數(shù)pxPortInitialiseStack()初始化任務(wù)堆棧,并將最新的棧頂指針賦值給任務(wù)TCB的pxTopOfStack字段。

          調(diào)用函數(shù)pxPortInitialiseStack()后,相當(dāng)于執(zhí)行了一次系統(tǒng)節(jié)拍時(shí)鐘中斷:將一些重要寄存器入棧。

          雖然任務(wù)還沒(méi)開(kāi)始執(zhí)行,也并沒(méi)有中斷發(fā)生,但看上去就像寄存器已經(jīng)被入棧了,并且部分堆棧值被修改成了我們需要的已知值。

          對(duì)于不同的硬件架構(gòu),入棧的寄存器也不相同,所以我們看到這個(gè)函數(shù)是由移植層提供的。

          對(duì)于Cortex-M3架構(gòu),需要依次入棧xPSR、PC、LR、R12、R3~R0、R11~R4,假設(shè)堆棧是向下生長(zhǎng)的,初始化后的堆棧如圖3-1所示。

          在圖3-1中我們看到寄存器xPSR被初始為0x01000000,其中bit24被置1,表示使用Thumb指令;

          寄存器PC被初始化為任務(wù)函數(shù)指針vTask_A,這樣當(dāng)某次任務(wù)切換后,任務(wù)A獲得CPU控制權(quán),任務(wù)函數(shù)vTask_A被出棧到PC寄存器,之后會(huì)執(zhí)行任務(wù)A的代碼;

          LR寄存器初始化為函數(shù)指針prvTaskExitError,這是由移植層提供的一個(gè)出錯(cuò)處理函數(shù)。

          當(dāng)中斷發(fā)生時(shí),LR被設(shè)置成中斷要返回的地址,但是每個(gè)任務(wù)都是一個(gè)死循環(huán),正常情況下不應(yīng)該退出任務(wù)函數(shù),所以一旦從任務(wù)函數(shù)退出,說(shuō)明那里出錯(cuò)了,這個(gè)時(shí)候會(huì)調(diào)用寄存器LR指向的函數(shù)來(lái)處理這個(gè)錯(cuò)誤,即prvTaskExitError;

          根據(jù)ATPCS(「ARM-Thumb過(guò)程調(diào)用標(biāo)準(zhǔn)」),我們知道子函數(shù)調(diào)用通過(guò)寄存器R0~R3傳遞參數(shù),在文章的最開(kāi)始講xTaskCreate()函數(shù)時(shí),提到這個(gè)函數(shù)有一個(gè)空指針類(lèi)型的參數(shù)pvParameters,當(dāng)任務(wù)創(chuàng)建時(shí),它作為一個(gè)參數(shù)傳遞給任務(wù),所以這個(gè)參數(shù)被保存到R0中,用來(lái)向任務(wù)傳遞參數(shù)。

          任務(wù)TCB結(jié)構(gòu)體成員pxTopOfStack表示當(dāng)前堆棧的棧頂,它指向最后一個(gè)入棧的項(xiàng)目,所以在圖中它指向R4,TCB結(jié)構(gòu)體另外一個(gè)成員pxStack表示堆棧的起始位置,所以在圖中它指向堆棧的最開(kāi)始處。

          圖3-1:初始化任務(wù)堆棧

          4.進(jìn)入臨界區(qū)

          調(diào)用taskENTER_CRITICAL()進(jìn)入臨界區(qū),這是一個(gè)宏定義,最終進(jìn)入臨界區(qū)的代碼由移植層提供。

          5.當(dāng)前任務(wù)數(shù)量增加1

          在tasks.c中 ,定義了一些靜態(tài)私有變量,用來(lái)跟蹤任務(wù)的數(shù)量或者狀態(tài)等等,其中變量uxCurrentNumberOfTasks表示當(dāng)前任務(wù)的總數(shù)量,每創(chuàng)建一個(gè)任務(wù),這個(gè)變量都會(huì)增加1。

          6.為第一次運(yùn)行做必要的初始化

          如果這是第一個(gè)任務(wù)(uxCurrentNumberOfTasks等于1),則調(diào)用函數(shù)prvInitialiseTaskLists()初始化任務(wù)列表。FreeRTOS使用列表來(lái)跟蹤任務(wù),在tasks.c中,定義了靜態(tài)類(lèi)型的列表變量:

          PRIVILEGED_DATAstatic List_t pxReadyTasksLists[ configMAX_PRIORITIES ];/*按照優(yōu)先級(jí)排序的就緒狀態(tài)任務(wù)*/
          PRIVILEGED_DATAstatic List_t xDelayedTaskList1;                        /*延時(shí)的任務(wù) */
          PRIVILEGED_DATAstatic List_t xDelayedTaskList2;                        /*延時(shí)的任務(wù) */
          PRIVILEGED_DATAstatic List_t xPendingReadyList;                        /*任務(wù)已就緒,但調(diào)度器被掛起 */
           
          #if (INCLUDE_vTaskDelete == 1 )
              PRIVILEGED_DATA static List_t xTasksWaitingTermination;             /*任務(wù)已經(jīng)被刪除,但內(nèi)存尚未釋放*/
          #endif
           
          #if (INCLUDE_vTaskSuspend == 1 )
              PRIVILEGED_DATA static List_t xSuspendedTaskList;                   /*當(dāng)前掛起的任務(wù)*/
          #endif

          現(xiàn)在這些列表都要進(jìn)行初始化,會(huì)調(diào)用API函數(shù)vListInitialise()初始化列表,這個(gè)函數(shù)在《FreeRTOS高級(jí)篇1---FreeRTOS列表和列表項(xiàng)》中講過(guò),每個(gè)列表的初始化方式都是相同的。

          以就緒態(tài)列表pxReadyTasksLists[0]為例,初始化后如圖6-1所示:

          圖6-1:初始化后的列表

          「函數(shù)prvInitialiseTaskLists()的源代碼如下所示:」

          static void prvInitialiseTaskLists( void )
          {
          UBaseType_tuxPriority;

            for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )
            {
                vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );
            }

            vListInitialise( &xDelayedTaskList1 );
            vListInitialise( &xDelayedTaskList2 );
            vListInitialise( &xPendingReadyList );

            #if ( INCLUDE_vTaskDelete == 1 )
            {
                vListInitialise( &xTasksWaitingTermination );
            }
            #endif /* INCLUDE_vTaskDelete */

            #if ( INCLUDE_vTaskSuspend == 1 )
            {
                vListInitialise( &xSuspendedTaskList );
            }
            #endif /* INCLUDE_vTaskSuspend */

            /* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskListusing list2. */
            pxDelayedTaskList = &xDelayedTaskList1;
            pxOverflowDelayedTaskList = &xDelayedTaskList2;
          }

          7.更新當(dāng)前正在運(yùn)行的任務(wù)TCB指針

          tasks.c中定義了一個(gè)任務(wù)TCB指針型變量:

          PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB= NULL;

          這是一個(gè)全局變量,在tasks.c中只定義了這一個(gè)全局變量。

          這個(gè)變量用來(lái)指向當(dāng)前正在運(yùn)行的任務(wù)TCB,我們需要多了解一下這個(gè)變量。

          FreeRTOS的核心是確保處于優(yōu)先級(jí)最高的就緒任務(wù)獲得CPU運(yùn)行權(quán)。

          在下一章講述任務(wù)切換時(shí)會(huì)知道,任務(wù)切換就是找到優(yōu)先級(jí)最高的就緒任務(wù),而找出的這個(gè)最高優(yōu)先級(jí)任務(wù)的TCB,就被賦給變量pxCurrentTCB。

          如果調(diào)度器還沒(méi)有準(zhǔn)備好(程序剛開(kāi)始運(yùn)行時(shí),可能會(huì)先創(chuàng)建幾個(gè)任務(wù),之后才會(huì)啟動(dòng)調(diào)度器),并且新創(chuàng)建的任務(wù)優(yōu)先級(jí)大于變量pxCurrentTCB指向的任務(wù)優(yōu)先級(jí),則設(shè)置pxCurrentTCB指向當(dāng)前新創(chuàng)建的任務(wù)TCB(確保pxCurrentTCB指向優(yōu)先級(jí)最高的就緒任務(wù))。

          if( xSchedulerRunning == pdFALSE )
          {
              if( pxCurrentTCB->uxPriority <= uxPriority )
              {
                  pxCurrentTCB = pxNewTCB;
              }
              else
              {
                  mtCOVERAGE_TEST_MARKER();
              }
          }

          8.將新創(chuàng)建的任務(wù)加入就緒列表數(shù)組

          調(diào)用prvAddTaskToReadyList(pxNewTCB)將創(chuàng)建的任務(wù)TCB加入到就緒列表數(shù)組中,任務(wù)的優(yōu)先級(jí)確定了加入到就緒列表數(shù)組的哪個(gè)下標(biāo)。

          比如我們新創(chuàng)建的任務(wù)優(yōu)先級(jí)為1,則這個(gè)任務(wù)被加入到列表pxReadyTasksLists[1]中。

          prvAddTaskToReadyList()其實(shí)是一個(gè)宏,由一系列語(yǔ)句組成,去除其中的跟蹤宏外,這個(gè)宏定義如下所示:

          #defineprvAddTaskToReadyList( pxTCB )                        \
              taskRECORD_READY_PRIORITY( ( pxTCB)->uxPriority );       \
              vListInsertEnd( &( pxReadyTasksLists[ (pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) );

          taskRECORD_READY_PRIORITY()用來(lái)更新變量uxTopReadyPriority,這個(gè)變量在tasks.c中定義為靜態(tài)變量,記錄處于就緒態(tài)的最高任務(wù)優(yōu)先級(jí)。

          這個(gè)變量「參與了FreeRTOS的最核心代碼」:確保處于優(yōu)先級(jí)最高的就緒任務(wù)獲得CPU運(yùn)行權(quán)。它在這里參與如何最快的找到優(yōu)先級(jí)最高的就緒任務(wù)。

          為了最快,不同的架構(gòu)會(huì)各顯神通,一些架構(gòu)還有特殊指令可用,所以這個(gè)宏由移植層提供。

          我們會(huì)在下一章介紹任務(wù)切換時(shí),以Cortex-M3架構(gòu)為例,詳細(xì)介紹如何最快的找到優(yōu)先級(jí)最高的就緒任務(wù)。

          函數(shù)vListInsertEnd()將列表項(xiàng)插入到列表末端,在《FreeRTOS高級(jí)篇1---FreeRTOS列表和列表項(xiàng)》中已經(jīng)提到過(guò),這里會(huì)結(jié)合著例子再看一下這個(gè)函數(shù)。

          從前面我們知道,在調(diào)用函數(shù)vListInsertEnd()之前,就緒列表pxReadyTasksLists[1]和任務(wù)TCB的狀態(tài)列表項(xiàng)xStateListItem都已經(jīng)初始化好了,見(jiàn)圖6-1和圖2-1,為了方便查看,我們將這兩幅圖合成一副,見(jiàn)圖8-1。

          圖8-1:初始化后的列表和列表項(xiàng)

          調(diào)用vListInsertEnd(a,b)會(huì)將列表項(xiàng)b,插入到列表a的后面,函數(shù)執(zhí)行完畢后,列表和列表項(xiàng)的關(guān)系如圖8-2所示。

          圖8-2:插入一個(gè)列表項(xiàng)后的列表

          在此基礎(chǔ)上,假設(shè)又創(chuàng)建了任務(wù)B,任務(wù)A和任務(wù)B優(yōu)先級(jí)相同,都為1。

          和任務(wù)A一樣,任務(wù)B也有它自己的任務(wù)TCB,其中的狀態(tài)列表項(xiàng)字段xStateListItem也要插入到列表pxReadyTasksLists[1]中,新的列表和列表項(xiàng)如圖8-3所示。

          圖8-3:相同優(yōu)先級(jí)就緒列表掛接兩個(gè)列表項(xiàng)

          9.退出臨界區(qū)

          調(diào)用taskEXIT_CRITICAL()退出臨界區(qū),這是一個(gè)宏定義,最終退出臨界區(qū)的代碼由移植層提供。

          10.執(zhí)行上下文切換

          如果上面的步驟都正確執(zhí)行,并且調(diào)度器也開(kāi)始工作,則判斷當(dāng)前任務(wù)的優(yōu)先級(jí)是否大于新創(chuàng)建的任務(wù)優(yōu)先級(jí)。

          如果新創(chuàng)建的任務(wù)優(yōu)先級(jí)更高,則調(diào)用taskYIELD_IF_USING_PREEMPTION()強(qiáng)制進(jìn)行一次上下文切換,切換后,新創(chuàng)建的任務(wù)將獲得CPU控制權(quán),精簡(jiǎn)后的代碼如下所示。

          if( xReturn == pdPASS )
          {
              if( xSchedulerRunning != pdFALSE )
              {
                  /* 如果新創(chuàng)建的任務(wù)優(yōu)先級(jí)大于當(dāng)前任務(wù)優(yōu)先級(jí),則新創(chuàng)建的任務(wù)應(yīng)該被立即執(zhí)行。*/
                  if(pxCurrentTCB->uxPriority < uxPriority )
                  {
                      taskYIELD_IF_USING_PREEMPTION();
                  }
              }
          }
          ????????????????  END  ????????????????

          推薦閱讀:


          嵌入式編程專(zhuān)輯
          Linux 學(xué)習(xí)專(zhuān)輯
          C/C++編程專(zhuān)輯
          Qt進(jìn)階學(xué)習(xí)專(zhuān)輯

           關(guān)注公眾號(hào)『技術(shù)讓夢(mèng)想更偉大』,后臺(tái)回復(fù)關(guān)鍵字:『Qt』『C語(yǔ)言基礎(chǔ)』『C語(yǔ)言難點(diǎn)』『C++』『Linux』『freertos』『指針』『數(shù)據(jù)結(jié)構(gòu)與算法』『經(jīng)驗(yàn)技巧篇』『疑問(wèn)篇』『基礎(chǔ)理論篇』『實(shí)戰(zhàn)篇』『架構(gòu)篇』『模塊化編程』『狀態(tài)機(jī)』『實(shí)用工具』『心聲社區(qū)』『期刊』『視頻』······等,查看更多精選內(nèi)容。 


          關(guān)注我的微信公眾號(hào),回復(fù)“加群”按規(guī)則加入技術(shù)交流群。


          這是我另一個(gè)技術(shù)號(hào),程序員的編程學(xué)習(xí)基地,注重編程思想,歡迎關(guān)注!


          點(diǎn)擊“閱讀原文”查看更多分享。

          瀏覽 90
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  波多野吉衣毛片 | 北条麻妃无码在线播放 | av第一二区 | 大香蕉伊人丁香五月 | 婷婷五缴天国产激情 |