返回顶部
首页 > 资讯 > 精选 >FreeRTOS任务创建的方法
  • 395
分享到

FreeRTOS任务创建的方法

2023-06-29 23:06:22 395人浏览 独家记忆
摘要

本篇内容介绍了“FreeRTOS任务创建的方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!我们这里先回顾一下这个函数的声明: &

本篇内容介绍了“FreeRTOS任务创建的方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

我们这里先回顾一下这个函数的声明:
        BaseType_t xTaskCreate(                            TaskFunction_tp vTaskCode,                            const char * constpcName,                            unsigned short usStackDepth,                            void *pvParameters,                            UBaseType_t uxPriority,                            TaskHandle_t *pvCreatedTask                          );

      这个api函数的作用是创建新的任务并将它加入到任务就绪列表,函数参数含义为:

pvTaskCode:函数指针,指向任务函数的入口。任务永远不会返回(位于死循环内)。该参数类型TaskFunction_t定义在文件projdefs.h中,定义为:typedef void(*TaskFunction_t)( void * ),即参数为空指针类型并返回空类型。pcName:任务描述。主要用于调试。字符串的最大长度(包括字符串结束字符)由宏configMAX_TASK_NAME_LEN指定,该宏位于FreeRTOSConfig.h文件中。usStackDepth:指定任务堆栈大小,能够支持的堆栈变量数量(堆栈深度),而不是字节数。比如,在16位宽度的堆栈下,usStackDepth定义为100,则实际使用200字节堆栈存储空间。堆栈的宽度乘以深度必须不超过size_t类型所能表示的最大值。比如,size_t为16位,则可以表示堆栈的最大值是65535字节。这是因为堆栈在申请时是以字节为单位的,申请的字节数就是堆栈宽度乘以深度,如果这个乘积超出size_t所表示的范围,就会溢出,分配的堆栈空间也不是我们想要的。pvParameters:指针,当任务创建时,作为一个参数传递给任务。uxPriority:任务的优先级。具有MPU支持的系统,可以通过置位优先级参数的portPRIVILEGE_BIT位,随意的在特权(系统)模式下创建任务。比如,创建一个优先级为2的特权任务,参数uxPriority可以设置为 ( 2 | portPRIVILEGE_BIT )。pvCreatedTask:用于回传一个句柄(ID),创建任务后可以使用这个句柄引用任务。

      虽然xTaskCreate()看上去很像函数,但其实是一个宏,真正被调用的函数是xTaskGenericCreate(),xTaskCreate()宏定义如下所示:

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

      可以看到,xTaskCreate比xTaskGenericCreate少了三个参数,在宏定义中,这三个参数被设置为NULL。这三个参数用于使用静态变量的方法分配堆栈、任务TCB空间以及设置MPU相关的参数。一般情况下,这三个参数是不使用的,所以任务创建宏xTaskCreate定义的时候,将这三个参数对用户隐藏了。接下来的章节中,为了方便,我们还是称xTaskCreate()为函数,虽然它是一个宏定义。

      上面我们提到了任务TCB(任务控制块),这是一个需要重点介绍的关键点。它用于存储任务的状态信息,包括任务运行时的环境。每个任务都有自己的任务TCB。任务TCB是一个相对比较大的数据结构,这也是情理之中的,因为与任务相关的代码占到整个FreeRTOS代码量的一半左右,这些代码大都与任务TCB相关,我们先来介绍一下任务TCB数据结构的定义:

typedef struct tskTaskControlBlock{    volatile StackType_t    *pxTopOfStack;      #if ( portUSING_MPU_WRAPPERS == 1 )        xMPU_SETTINGS   xMPUSettings;          #endif     ListItem_t          xStateListItem;     ListItem_t          xEventListItem;        UBaseType_t         uxPriority;            StackType_t         *pxStack;               char               pcTaskName[ configMAX_TASK_NAME_LEN ];     #if ( portSTACK_GROWTH > 0 )        StackType_t     *pxEndOfStack;         #endif     #if ( portCRITICAL_NESTING_IN_TCB == 1 )        UBaseType_t     uxCriticalNesting;     #endif     #if ( configUSE_TRACE_FACILITY == 1 )        UBaseType_t     uxTCBNumber;               UBaseType_t     uxTaskNumber;          #endif     #if ( configUSE_MUTEXES == 1 )        UBaseType_t     uxBasePriority;            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;      #endif     #if ( configUSE_NEWLIB_REENTRANT == 1 )                struct _reent xNewLib_reent;    #endif     #if( configUSE_TASK_NOTIFICATIONS == 1 )        volatile uint32_t ulNotifiedValue;         volatile uint8_t ucNotifyState;    #endif     #if( configSUPPORT_STATIC_ALLOCATION == 1 )        uint8_t ucStaticAllocationFlags;     #endif     #if( INCLUDE_xTaskAbortDelay == 1 )        uint8_t ucDelayAborted;    #endif } tskTCB; typedef tskTCB TCB_t;

      下面我们详细的介绍这个数据结构的主要成员:

      指针pxTopOfStack必须位于结构体的第一项,指向当前堆栈的栈顶,对于向下增长的堆栈,pxTopOfStack总是指向最后一个入栈的项目

      如果使用MPU,xMPUSettings必须位于结构体的第二项,用于MPU设置。

      接下来是状态列表项xStateListItem和事件列表项xEventListItem,我们在上一章介绍列表和列表项的文章中提到过:列表被FreeRTOS调度器使用,用于跟踪任务,处于就绪、挂起、延时的任务,都会被挂接到各自的列表中。调度器就是通过把任务TCB中的状态列表项xStateListItem和事件列表项xEventListItem挂接到不同的列表中来实现上述过程的。在task.c中,定义了一些静态列表变量,其中有就绪、阻塞、挂起列表,例如当某个任务处于就绪态时,调度器就将这个任务TCB的xStateListItem列表项挂接到就绪列表。事件列表项也与之类似,当队列满的情况下,任务因入队操作而阻塞时,就会将事件列表项挂接到队列的等待入队列表上。

      uxPriority用于保存任务的优先级,0为最低优先级。任务创建时,指定的任务优先级就被保存到该变量中。

      指针pxStack指向堆栈的起始位置,任务创建时会分配指定数目的任务堆栈,申请堆栈内存函数返回的指针就被赋给该变量。很多刚接触FreeRTOS的人会分不清指针pxTopOfStack和pxStack的区别,这里简单说一下:pxTopOfStack指向当前堆栈栈顶,随着进栈出栈,pxTopOfStack指向的位置是会变化的;pxStack指向当前堆栈的起始位置,一经分配后,堆栈起始位置就固定了,不会被改变了。那么为什么需要pxStack变量呢,这是因为随着任务的运行,堆栈可能会溢出,在堆栈向下增长的系统中,这个变量可用于检查堆栈是否溢出;如果在堆栈向上增长的系统中,要想确定堆栈是否溢出,还需要另外一个变量pxEndOfStack来辅助诊断是否堆栈溢出,后面会讲到这个变量。

      字符数组pcTaskName用于保存任务的描述或名字,在任务创建时,由参数指定。名字的长度由宏configMAX_TASK_NAME_LEN(位于FreeRTOSConfig.h中)指定,包含字符串结束标志。

      如果堆栈向上生长(portSTACK_GROWTH > 0),指针pxEndOfStack指向堆栈尾部,用于检验堆栈是否溢出。

      变量uxCriticalNesting用于保存临界区嵌套深度,初始值为0。

      接下来两个变量用于可视化追踪,仅当宏configUSE_TRACE_FACILITY(位于FreeRTOSConfig.h中)为1时有效。变量uxTCBNumber存储一个数值,在创建任务时由内核自动分配数值(通常每创建一个任务,值增加1),每个任务的uxTCBNumber值都不同,主要用于调试。变量uxTaskNumber用于存储一个特定值,与变量uxTCBNumber不同,uxTaskNumber的数值不是由内核分配的,而是通过API函数vTaskSetTaskNumber()来设置的,数值由函数参数指定。

      如果使用互斥量(configUSE_MUTEXES == 1),任务优先级被临时提高时,变量uxBasePriority用来保存任务原来的优先级。

      变量ucStaticAllocationFlags也需要说明一下,我们前面说过任务创建API函数xTaskCreate()只能使用动态内存分配的方式创建任务堆栈和任务TCB,如果要使用静态变量实现任务堆栈和任务TCB就需要使用函数xTaskGenericCreate()来实现。如果任务堆栈或任务TCB由静态数组和静态变量实现,则将该变量设置为pdTRUE(任务堆栈空间由静态数组变量实现时为0x01,任务TCB由静态变量实现时为0x02,任务堆栈和任务TCB都由静态变量实现时为0x03),如果堆栈是动态分配的,则将该变量设置为pdfALSE。

      到这里任务TCB的数据结构就讲完了,下面我们用一个例子来讲述任务创建的过程,为方便起见,假设被创建的任务叫“任务A”,任务函数为vTask_A():

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

      这里创建了一个任务,任务优先级为1,由于硬件平台是32为架构,所以指定了120*4=480字节的任务堆栈,向任务函数vTask_A()传递的参数为空(NULL),任务句柄由变量xHandle保存。当这个语句执行后,任务A被创建并加入就绪任务列表,我们这章的主要目的,就是看看这个语句在执行过程中,发生了什么事情。

创建任务堆栈和任务TCB

      调用函数prvAllocateTCBAndStack()创建任务堆栈和任务TCB。有两种方式创建任务堆栈和任务TCB,一种是使用动态内存分配方法,这样当任务删除时,任务堆栈和任务控制块空间会被释放,可用于其它任务;另一种是使用静态变量来实现,在创建任务前定义好全局或者静态堆栈数组和任务控制块变量,在调用创建任务API函数时,将这两个变量以参数的形式传递给任务创建函数xTaskGenericCreate()。如果使用默认的xTaskCreate()创建任务函数,则使用动态内存分配,因为与静态内存分配有关的参数不可见(在本文一开始我们说过xTaskCreate()其实是一个带参数的宏定义,真正被执行的函数是xTaskGenericCreate(),参考宏xTaskCreate()的定义可以知道,xTaskCreate()对外隐藏了使用静态内存分配的参数,在调用xTaskGenericCreate()时,这些参数被设置为NULL)。

      任务堆栈成功分配后,经过对齐的堆栈起始地址被保存到任务TCB的pxStack字段。如果使能堆栈溢出检查或者使用可视化追踪功能,则使用固定值tskSTACK_FILL_BYTE(0xa5)填充堆栈。

      函数prvAllocateTCBAndStack()的源码去除断言和不常用的条件编译后如下所示:

static TCB_t *prvAllocateTCBAndStack( const uint16_t usStackDepth, StackType_t * const puxStackBuffer, TCB_t * const pxTaskBuffer ){TCB_t *pxNewTCB;StackType_t *pxStack;         pxStack = ( StackType_t * ) pvPortMallocAligned( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ), puxStackBuffer );    if( pxStack != NULL )    {                pxNewTCB = ( TCB_t * ) pvPortMallocAligned( sizeof( TCB_t ), pxTaskBuffer );         if( pxNewTCB != NULL )        {                        pxNewTCB->pxStack = pxStack;        }        else        {                        if( puxStackBuffer == NULL )            {                vPortFree( pxStack );            }        }    }    else    {        pxNewTCB = NULL;    }     if( pxNewTCB != NULL )    {                #if( ( confiGCHECK_FOR_STACK_OVERFLOW> 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark== 1 ) )        {                        ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) usStackDepth * sizeof( StackType_t ) );        }        #endif    }     return pxNewTCB;}

初始化任务TCB必要的字段

      调用函数prvInitialiseTCBVariables()初始化任务TCB必要的字段。在调用创建任务API函数xTaskCreate()时,参数pcName(任务描述)、uxPriority(任务优先级)都会被写入任务TCB相应的字段,TCB字段中的xStateListItem和xEventListItem列表项也会被初始化,初始化后的列表项如图2-1所示。在图2-1中,列表项xEventListItem的成员列表项值xItemValue被初始为4,这是因为我在应用中设置的最大优先级数目(configMAX_PRIORITIES)为5,而xEventListItem. xItemValue等于configMAX_PRIORITIES减去任务A的优先级(为1),即5-1=4。这一点很重要,在这里xItemValue不是直接保存任务优先级,而是保存优先级的补数,这意味着xItemValue的值越大,对应的任务优先级越小。FreeRTOS内核使用vListInsert函数(详细见高级篇第一章)将事件列表项插入到一个列表,这个函数根据xItemValue的值的大小顺序来进行插入操作。使用宏listGET_OWNER_OF_HEAD_ENTRY获得列表中的第一个列表项的xItemValue值总是最小,也就是优先级最高的任务!

FreeRTOS任务创建的方法

图2-1:初始化状态和事件列表项

      此外,TCB其它的一些字段也被初始化,比如临界区嵌套次数、运行时间计数器、任务通知值、任务通知状态等,函数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;         for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )    {        pxTCB->pcTaskName[ x ] = pcName[ x ];        if( pcName[ x ] == 0x00 )        {            break;        }    }        pxTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';         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            vListInitialiseItem( &( pxTCB->xStateListItem ) );    vListInitialiseItem( &( pxTCB->xEventListItem ) );         listSET_LIST_ITEM_OWNER( &( pxTCB->xStateListItem ), pxTCB );         listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority );        listSET_LIST_ITEM_OWNER( &( pxTCB->xEventListItem ), pxTCB );     #if ( portCRITICAL_NESTING_IN_TCB ==1 )        {          pxTCB->uxCriticalNesting = ( UBaseType_t ) 0U;    }    #endif      #if ( configUSE_APPLICATION_TASK_TAG == 1 )     {          pxTCB->pxTaskTag = NULL;    }    #endif      #if ( configGENERATE_RUN_TIME_STATS == 1 )      {        pxTCB->ulRunTimeCounter = 0UL;    }    #endif      #if ( portUSING_MPU_WRAPPERS == 1 )             {        vPortStoreTaskMPUSettings( &( pxTCB->xMPUSettings ), xRegions, pxTCB->pxStack, usStackDepth );    }    #else     {        ( void ) xRegions;        ( void ) usStackDepth;    }    #endif      #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 )    {        for( x = 0; x < ( UBaseType_t )configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ )        {            pxTCB->pvThreadLocalStoragePointers[ x ] = NULL;        }    }    #endif     #if ( configUSE_TASK_NOTIFICATIONS == 1 )       {        pxTCB->ulNotifiedValue = 0;        pxTCB->ucNotifyState = taskNOT_WaiTING_NOTIFICATION;    }    #endif     #if ( configUSE_NEWLIB_REENTRANT == 1 )         {        _REENT_INIT_PTR( ( &( pxTCB->xNewLib_reent ) ) );    }    #endif     #if( INCLUDE_xTaskAbortDelay == 1 )    {        pxTCB->ucDelayAborted = pdFALSE;    }    #endif}

初始化任务堆栈

      调用函数pxPortInitialiseStack()初始化任务堆栈,并将最新的栈顶指针赋值给任务TCB的pxTopOfStack字段。

      调用函数pxPortInitialiseStack()后,相当于执行了一次系统节拍时钟中断:将一些重要寄存器入栈。虽然任务还没开始执行,也并没有中断发生,但看上去就像寄存器已经被入栈了,并且部分堆栈值被修改成了我们需要的已知值。对于不同的硬件架构,入栈的寄存器也不相同,所以我们看到这个函数是由移植层提供的。对于Cortex-M3架构,需要依次入栈xPSR、PC、LR、R12、R3~R0、R11~R4,假设堆栈是向下生长的,初始化后的堆栈如图3-1所示。

      在图3-1中我们看到寄存器xPSR被初始为0x01000000,其中bit24被置1,表示使用Thumb指令;寄存器PC被初始化为任务函数指针vTask_A,这样当某次任务切换后,任务A获得CPU控制权,任务函数vTask_A被出栈到PC寄存器,之后会执行任务A的代码;LR寄存器初始化为函数指针prvTaskExitError,这是由移植层提供的一个出错处理函数。当中断发生时,LR被设置成中断要返回的地址,但是每个任务都是一个死循环,正常情况下不应该退出任务函数,所以一旦从任务函数退出,说明那里出错了,这个时候会调用寄存器LR指向的函数来处理这个错误,即prvTaskExitError;根据ATPCS(ARM-Thumb过程调用标准),我们知道子函数调用通过寄存器R0~R3传递参数,在文章的最开始讲xTaskCreate()函数时,提到这个函数有一个空指针类型的参数pvParameters,当任务创建时,它作为一个参数传递给任务,所以这个参数被保存到R0中,用来向任务传递参数。

      任务TCB结构体成员pxTopOfStack表示当前堆栈的栈顶,它指向最后一个入栈的项目,所以在图中它指向R4,TCB结构体另外一个成员pxStack表示堆栈的起始位置,所以在图中它指向堆栈的最开始处。

FreeRTOS任务创建的方法

图3-1:初始化任务堆栈

进入临界区

      调用taskENTER_CRITICAL()进入临界区,这是一个宏定义,最终进入临界区的代码由移植层提供。

当前任务数量增加1

      在tasks.c中 ,定义了一些静态私有变量,用来跟踪任务的数量或者状态等等,其中变量uxCurrentNumberOfTasks表示当前任务的总数量,每创建一个任务,这个变量都会增加1。

为第一次运行做必要的初始化

      如果这是第一个任务(uxCurrentNumberOfTasks等于1),则调用函数prvInitialiseTaskLists()初始化任务列表。FreeRTOS使用列表来跟踪任务,在tasks.c中,定义了静态类型的列表变量:

PRIVILEGED_DATAstatic List_t pxReadyTasksLists[ configMAX_PRIORITIES ];PRIVILEGED_DATAstatic List_t xDelayedTaskList1;                        PRIVILEGED_DATAstatic List_t xDelayedTaskList2;                        PRIVILEGED_DATAstatic List_t xPendingReadyList;                         #if (INCLUDE_vTaskDelete == 1 )    PRIVILEGED_DATA static List_t xTasksWaitingTermination;             #endif #if (INCLUDE_vTaskSuspend == 1 )    PRIVILEGED_DATA static List_t xSuspendedTaskList;                   #endif

      现在这些列表都要进行初始化,会调用API函数vListInitialise()初始化列表,这个函数在《FreeRTOS高级篇1---FreeRTOS列表和列表项》中讲过,每个列表的初始化方式都是相同的,以就绪态列表pxReadyTasksLists[0]为例,初始化后如图6-1所示:

FreeRTOS任务创建的方法

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

      函数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      #if ( INCLUDE_vTaskSuspend == 1 )    {        vListInitialise( &xSuspendedTaskList );    }    #endif          pxDelayedTaskList = &xDelayedTaskList1;    pxOverflowDelayedTaskList = &xDelayedTaskList2;}

更新当前正在运行的任务TCB指针

      tasks.c中定义了一个任务TCB指针型变量:

      PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB= NULL;

      这是一个全局变量,在tasks.c中只定义了这一个全局变量。这个变量用来指向当前正在运行的任务TCB,我们需要多了解一下这个变量。FreeRTOS的核心是确保处于优先级最高的就绪任务获得CPU运行权。在下一章讲述任务切换时会知道,任务切换就是找到优先级最高的就绪任务,而找出的这个最高优先级任务的TCB,就被赋给变量pxCurrentTCB。

      如果调度器还没有准备好(程序刚开始运行时,可能会先创建几个任务,之后才会启动调度器),并且新创建的任务优先级大于变量pxCurrentTCB指向的任务优先级,则设置pxCurrentTCB指向当前新创建的任务TCB(确保pxCurrentTCB指向优先级最高的就绪任务)。

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

将新创建的任务加入就绪列表数组

      调用prvAddTaskToReadyList(pxNewTCB)将创建的任务TCB加入到就绪列表数组中,任务的优先级确定了加入到就绪列表数组的哪个下标。比如我们新创建的任务优先级为1,则这个任务被加入到列表pxReadyTasksLists[1]中。

       prvAddTaskToReadyList()其实是一个宏,由一系列语句组成,去除其中的跟踪宏外,这个宏定义如下所示:

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

      宏taskRECORD_READY_PRIORITY()用来更新变量uxTopReadyPriority,这个变量在tasks.c中定义为静态变量,记录处于就绪态的最高任务优先级。这个变量参与了FreeRTOS的最核心代码:确保处于优先级最高的就绪任务获得CPU运行权。它在这里参与如何最快的找到优先级最高的就绪任务。为了最快,不同的架构会各显神通,一些架构还有特殊指令可用,所以这个宏由移植层提供。我们会在下一章介绍任务切换时,以Cortex-M3架构为例,详细介绍如何最快的找到优先级最高的就绪任务。

      函数vListInsertEnd()将列表项插入到列表末端,在《FreeRTOS高级篇1---FreeRTOS列表和列表项》中已经提到过,这里会结合着例子再看一下这个函数。从前面我们直到,在调用函数vListInsertEnd()之前,就绪列表pxReadyTasksLists[1]和任务TCB的状态列表项xStateListItem都已经初始化好了,见图6-1和图2-1,为了方便查看,我们将这两幅图合成一副,见图8-1。

FreeRTOS任务创建的方法

图8-1:初始化后的列表和列表项

         调用vListInsertEnd(a,b)会将列表项b,插入到列表a的后面,函数执行完毕后,列表和列表项的关系如图8-2所示。

FreeRTOS任务创建的方法

图8-2:插入一个列表项后的列表

      在此基础上,假设又创建了任务B,任务A和任务B优先级相同,都为1。和任务A一样,任务B也有它自己的任务TCB,其中的状态列表项字段xStateListItem也要插入到列表pxReadyTasksLists[1]中,新的列表和列表项如图8-3所示。

FreeRTOS任务创建的方法

图8-3:相同优先级就绪列表挂接两个列表项

退出临界区

      调用taskEXIT_CRITICAL()退出临界区,这是一个宏定义,最终退出临界区的代码由移植层提供。

执行上下文切换

    如果上面的步骤都正确执行,并且调度器也开始工作,则判断当前任务的优先级是否大于新创建的任务优先级。如果新创建的任务优先级更高,则调用taskYIELD_IF_USING_PREEMPTION()强制进行一次上下文切换,切换后,新创建的任务将获得CPU控制权,精简后的代码如下所示。

 if( xReturn == pdPASS )    {        if( xSchedulerRunning != pdFALSE )        {                        if(pxCurrentTCB->uxPriority < uxPriority )            {                taskYIELD_IF_USING_PREEMPTION();            }        }    }

“FreeRTOS任务创建的方法”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

--结束END--

本文标题: FreeRTOS任务创建的方法

本文链接: https://lsjlt.com/news/326660.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • FreeRTOS任务创建的方法
    本篇内容介绍了“FreeRTOS任务创建的方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!我们这里先回顾一下这个函数的声明: &...
    99+
    2023-06-29
  • FreeRTOS实时操作系统的任务创建与任务切换
    目录任务控制块数据结构任务创建函数定义就绪表就绪表初始化启动调度器任务切换    任务控制块数据结构 任务控制块数据结构在task.c声明 typedef str...
    99+
    2024-04-02
  • FreeRTOS进阶之任务创建完全解析
    目录前言1.创建任务堆栈和任务TCB2.初始化任务TCB必要的字段3.初始化任务堆栈4.进入临界区5.当前任务数量增加16.为第一次运行做必要的初始化7.更新当前正在运行的任务TCB...
    99+
    2024-04-02
  • FreeRTOS实时操作系统的任务创建和删除
    目录前言 1.任务创建1.1函数描述1.2参数描述1.3返回值1.4用法举例2.任务删除2.1任务描述2.2参数描述前言  在FreeRTOS移植到Cortex-...
    99+
    2024-04-02
  • FreeRTOS实时操作系统的任务怎么创建和删除
    本文小编为大家详细介绍“FreeRTOS实时操作系统的任务怎么创建和删除”,内容详细,步骤清晰,细节处理妥当,希望这篇“FreeRTOS实时操作系统的任务怎么创建和删除”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧...
    99+
    2023-06-29
  • FreeRTOS实时操作系统的任务通知方法
    目录前言1.发送通知-方法11.1函数描述1.2参数描述1.3返回值2.发送通知-方法22.1函数描述2.2参数描述2.3用法举例3.获取通知3.1函数描述3.2参数描述3.3返回值...
    99+
    2024-04-02
  • navicat创建MySql定时任务的方法详解
    navicat创建MySql 定时任务详解 事件(event)是MySQL在相应的时刻调用的过程式数据库对象。一个事件可调用一次,也可周期性的启动,它由一个特定的线程来管理的,也就是所谓的“事件调度器”。事件和触发器...
    99+
    2022-05-31
    navicat MySql定时任务 navicat MySql
  • MySQL创建定时任务的方法是什么
    今天小编给大家分享一下MySQL创建定时任务的方法是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来...
    99+
    2023-03-06
    mysql
  • phpYii2框架创建定时任务方法详解
    目录简单的定时任务脚步文件传递参数设置参数别名在Linux配置crontab在我们项目开发中,需要使用定时任务来完成批处理业务,下面通过Yii2框架来实现定时任务 Yii2框架定时任...
    99+
    2024-04-02
  • java动态创建定时任务的方法是什么
    Java中动态创建定时任务的方法可以通过使用ScheduledExecutorService接口来实现。ScheduledExecu...
    99+
    2023-08-15
    java
  • MySQL 定时任务( Navicat创建方式)
    1.先看MySQL是否开启时间(off 开启,no 关闭) 如果是关闭状态执行 方法1:set GLOBAL event_scheduler = 1; 修改支持所有会话,但是MySQL重启就会失效 方法2:编辑my.cnf(/etc/m...
    99+
    2023-09-08
    java mysql
  • Linux中使用crond工具创建定时任务的方法
    前言 crond是一个linux下的定时执行工具(相当于windows下的scheduled task),可以在无需人工干预的情况下定时地运行任务。crond工具提供crontab命令来设置定时任务,属于守护进程,...
    99+
    2022-06-04
    linux 创建定时任务 linux crond工具
  • Win8创建任务计划操作方法图文教程
      有很多用户安装一些定时执行任务的软件,用来完成定时播放歌曲等行为。其实直接用系统自带的任务计划程序也可以做到,下面我们来看看这在Win8上如何操作。   操作步骤   1、首先同时按下Windows键...
    99+
    2022-06-04
    操作方法 图文 计划
  • Laravel创建定时任务
            创建一个任务,创建成功后会在App/Console/Commands中生成一个以Test命名的文件,我们可以在这里面写我们的任务指令。 php artisan make:command Test         运行这...
    99+
    2023-09-02
    laravel php
  • Laravel8创建定时任务
    序言         这篇文章讲述的是一种最简单的创建定时任务的操作。 开始         Laravel8文档 Laravel8中文文档         创建一个任务,创建成功后会在App/Console/Commands中生成一个以T...
    99+
    2023-09-13
    php laravel
  • Yii2 创建定时任务
    在我们项目开发中,需要使用定时任务来完成批处理业务,下面通过Yii2框架来实现定时任务 Yii2框架定时任务代码统一放在 ./console 目录下 简单的定时任务脚步文件 首先在console/co...
    99+
    2023-08-31
    php Yii
  • MySQL创建定时任务
    定时任务是老生常谈了,因为我们总是需要定时修改特定的数据。 实现它的方法肯定不止一种,但我在相当长一段时间里都是用程序编码去做的,今天突然想到“为什么一定要采用调用的方式...
    99+
    2024-04-02
  • 为Jenkins创建定时构建任务
    目录一、前言二、创建定义任务1、新建任务2、General3、源码管理4、构建触发器5、构建环境6、构建7、构建后操作三、总结一、前言 在前面的文章中,我们添加SSH凭据,添加了凭据...
    99+
    2024-04-02
  • FreeRTOS任务控制API函数的功能分析
    目录1.相对延时1.1函数描述1.2参数描述1.3用法举例2.绝对延时2.1函数描述2.2参数描述2.3用法举例3.获取任务优先级3.1函数描述3.2参数描述3.3返回值3.4用法举...
    99+
    2024-04-02
  • FreeRTOS实时操作系统的任务是什么
    这篇文章主要介绍了FreeRTOS实时操作系统的任务是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇FreeRTOS实时操作系统的任务是什么文章都会有所收获,下面我们一起来看看吧。1. 任务和协程(Co-r...
    99+
    2023-06-29
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作