FreeRTOS记录(二、FreeRTOS任务API认识和源码简析)

简介: 在了解了基本的环境和框架之后,对FreeRTOS 的任务,消息队列,信号量,事件,软件定时器这些基础的功能部分也得有个认识。 这篇文章主要介绍了一下关于任务的API以及源码的简单分析。
在了解了基本的环境和框架之后,对FreeRTOS 的任务,消息队列,信号量,事件,软件定时器
这些基础的功能部分也得有个认识。 
这篇文章主要介绍了一下关于任务的API以及源码的简单分析。 

前言

上文我们开始接触了 STM32CubeMX 下 FreeRTOS 的基本的使用环境和框架,从本文开始我们就开始进行一些 FreeRTOS 的基本知识学习

本 FreeRTOS 专栏记录的开发环境:
FreeRTOS记录(一、熟悉开发环境以及CubeMX下FreeRTOS配置)

FreeRTOS 任务API

来认识一下FreeRTOS的常用任务API:

API名称 CMSIS封装的API API说明
xTaskCreate osThreadCreate 动态创建任务
xTaskCreateStatic osThreadCreate 静态创建任务
vTaskDelete osThreadTerminate 删除任务
vTaskSuspend osThreadSuspend 挂起任务
vTaskResume osThreadResume 恢复任务
xTaskResumeFromISR osThreadResume 在中断中恢复任务
uxTaskPriorityGet osThreadGetPriority 获取任务优先级
vTaskPrioritySet osThreadSetPriority 设置任务优先级
vTaskDelay osDelay 相对延时任务
vTaskDelayuntil osDelayUntil 绝对延时任务

创建任务

任务创建分为动态xTaskCreate和静态xTaskCreateStatic ,但是在CubeMX中通过封装后统一使用的是osThreadCreate ,可以查看一下osThreadCreate 实现:

/*********************** Thread Management *****************************/
/**
* @brief  Create a thread and add it to Active Threads and set it to state READY.
* @param  thread_def    thread definition referenced with \ref osThread.
* @param  argument      pointer that is passed to the thread function as start argument.
* @retval thread ID for reference by other functions or NULL in case of error.
* @note   MUST REMAIN UNCHANGED: \b osThreadCreate shall be consistent in every CMSIS-RTOS.
*/
osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument)
{
  TaskHandle_t handle;
  
#if( configSUPPORT_STATIC_ALLOCATION == 1 ) &&  ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
  if((thread_def->buffer != NULL) && (thread_def->controlblock != NULL)) {
    handle = xTaskCreateStatic((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
              thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
              thread_def->buffer, thread_def->controlblock);
  }
  else {
    if (xTaskCreate((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
              thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
              &handle) != pdPASS)  {
      return NULL;
    } 
  }
#elif( configSUPPORT_STATIC_ALLOCATION == 1 )

    handle = xTaskCreateStatic((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
              thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
              thread_def->buffer, thread_def->controlblock);
#else
  if (xTaskCreate((TaskFunction_t)thread_def->pthread,(const portCHAR *)thread_def->name,
                   thread_def->stacksize, argument, makeFreeRtosPriority(thread_def->tpriority),
                   &handle) != pdPASS)  {
    return NULL;
  }     
#endif
  
  return handle;
}

由上可以得知,CubeMX生成的osThreadCreate 会根据我们在Config Parameters中的Memory Allocation内存分配中选择的配置结果自动的选择xTaskCreate还是xTaskCreateStatic

xTaskCreate
在这里插入图片描述xTaskCreateStatic
在这里插入图片描述

删除任务

vTaskDelete封装后是osThreadTerminate ,如下:

osStatus osThreadTerminate (osThreadId thread_id)
{
#if (INCLUDE_vTaskDelete == 1)
  vTaskDelete(thread_id);
  return osOK;
#else
  return osErrorOS;
#endif
}

参数为NULL vTaskDelete(NULL)是删除任务自己

挂起任务

void vTaskSuspend( TaskHandle_t xTaskToSuspend )传入的参数是 任务句柄

vTaskSuspend封装后是osThreadSuspend ,如下:

//
osStatus osThreadSuspend (osThreadId thread_id)
{
#if (INCLUDE_vTaskSuspend == 1)
    vTaskSuspend(thread_id);
  
  return osOK;
#else
  return osErrorResource;
#endif
}

参数为NULL vTaskSuspend(NULL)是挂起任务自己

osThreadSuspendAll 挂起所有任务

osStatus osThreadSuspendAll (void)
{
  vTaskSuspendAll();
  
  return osOK;
}

恢复任务

vTaskResume在任务中恢复任务(将任务从挂起状态恢复到就绪态)
xTaskResumeFromISR在中断中恢复任务封装后是osThreadResume,如下:

osStatus osThreadResume (osThreadId thread_id)
{
#if (INCLUDE_vTaskSuspend == 1)  
  if(inHandlerMode())
  {
    if (xTaskResumeFromISR(thread_id) == pdTRUE)
    {
      portYIELD_FROM_ISR(pdTRUE);
    }
  }
  else
  {
    vTaskResume(thread_id);
  }
  return osOK;
#else
  return osErrorResource;
#endif
}

osThreadResumeAll 恢复所有任务

osStatus osThreadResumeAll (void)
{
  if (xTaskResumeAll() == pdTRUE)
    return osOK;
  else
    return osErrorOS;
  
}

任务优先级

uxTaskPriorityGet获取任务优先级 封装后是osThreadGetPriority,还有一个从中断中获取没有写出来,但是通过封装后的函数可以看出来:

osPriority osThreadGetPriority (osThreadId thread_id)
{
#if (INCLUDE_uxTaskPriorityGet == 1)
  if (inHandlerMode())
  {
    return makeCmsisPriority(uxTaskPriorityGetFromISR(thread_id));  
  }
  else
  {  
    return makeCmsisPriority(uxTaskPriorityGet(thread_id));
  }
#else
  return osPriorityError;
#endif
}

vTaskPrioritySet设置任务优先级 封装后是osThreadSetPriority,如下:

osStatus osThreadSetPriority (osThreadId thread_id, osPriority priority)
{
#if (INCLUDE_vTaskPrioritySet == 1)
  vTaskPrioritySet(thread_id, makeFreeRtosPriority(priority));
  return osOK;
#else
  return osErrorOS;
#endif
}

延时任务

vTaskDelay相对延时封装后osDelay,如下:

/*********************** Generic Wait Functions *******************************/
/**
* @brief   Wait for Timeout (Time Delay)
* @param   millisec      time delay value
* @retval  status code that indicates the execution status of the function.
*/
osStatus osDelay (uint32_t millisec)
{
#if INCLUDE_vTaskDelay
  TickType_t ticks = millisec / portTICK_PERIOD_MS;
  
  vTaskDelay(ticks ? ticks : 1);          /* Minimum delay = 1 tick */
  
  return osOK;
#else
  (void) millisec;
  
  return osErrorResource;
#endif
}

vTaskDelayuntil绝对延时封装后osDelayUntil,如下:

/**
* @brief  Delay a task until a specified time
* @param   PreviousWakeTime   Pointer to a variable that holds the time at which the 
*          task was last unblocked. PreviousWakeTime must be initialised with the current time
*          prior to its first use (PreviousWakeTime = osKernelSysTick() )
* @param   millisec    time delay value
* @retval  status code that indicates the execution status of the function.
*/
osStatus osDelayUntil (uint32_t *PreviousWakeTime, uint32_t millisec)
{
#if INCLUDE_vTaskDelayUntil
  TickType_t ticks = (millisec / portTICK_PERIOD_MS);
  vTaskDelayUntil((TickType_t *) PreviousWakeTime, ticks ? ticks : 1);
  
  return osOK;
#else
  (void) millisec;
  (void) PreviousWakeTime;
  
  return osErrorResource;
#endif
}

FreeRTOS 任务源码简析

任务状态

在分析任务源码之前,先得了解一下FreeRTOS中任务的四种基本状态:
运行态,挂起态,阻塞态,就绪态。

在官方文档中:
在这里插入图片描述

任务控制块

FreeRTOS 的每个任务都有一些属性需要存储,把这些属性集合在一起的结构体叫做任务控制块TCB_t (Task control block),源码如下:

/*
 * Task control block.  A task control block (TCB) is allocated for each task,
 * and stores task state information, including a pointer to the task's context
 * (the task's run time environment, including register values)
 */
typedef struct tskTaskControlBlock
{
    volatile StackType_t    *pxTopOfStack;    /*< Points to the location of the last item placed on the tasks stack.  THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */

    #if ( portUSING_MPU_WRAPPERS == 1 )
        xMPU_SETTINGS    xMPUSettings;        /*< The MPU settings are defined as part of the port layer.  THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */
    #endif

    ListItem_t            xStateListItem;    /*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */
    ListItem_t            xEventListItem;        /*< Used to reference a task from an event list. */
    UBaseType_t            uxPriority;            /*< The priority of the task.  0 is the lowest priority. */
    StackType_t            *pxStack;            /*< Points to the start of the stack. */
    char                pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created.  Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */

    #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
        StackType_t        *pxEndOfStack;        /*< Points to the highest valid address for the stack. */
    #endif

    #if ( portCRITICAL_NESTING_IN_TCB == 1 )
        UBaseType_t        uxCriticalNesting;    /*< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */
    #endif

    #if ( configUSE_TRACE_FACILITY == 1 )
        UBaseType_t        uxTCBNumber;        /*< Stores a number that increments each time a TCB is created.  It allows debuggers to determine when a task has been deleted and then recreated. */
        UBaseType_t        uxTaskNumber;        /*< Stores a number specifically for use by third party trace code. */
    #endif

    #if ( configUSE_MUTEXES == 1 )
        UBaseType_t        uxBasePriority;        /*< The priority last assigned to the task - used by the priority inheritance mechanism. */
        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;    /*< Stores the amount of time the task has spent in the Running state. */
    #endif

    #if ( configUSE_NEWLIB_REENTRANT == 1 )
        /* Allocate a Newlib reent structure that is specific to this task.
        Note Newlib support has been included by popular demand, but is not
        used by the FreeRTOS maintainers themselves.  FreeRTOS is not
        responsible for resulting newlib operation.  User must be familiar with
        newlib and must provide system-wide implementations of the necessary
        stubs. Be warned that (at the time of writing) the current newlib design
        implements a system-wide malloc() that must be provided with locks. */
        struct    _reent xNewLib_reent;
    #endif

    #if( configUSE_TASK_NOTIFICATIONS == 1 )
        volatile uint32_t ulNotifiedValue;
        volatile uint8_t ucNotifyState;
    #endif

    /* See the comments above the definition of
    tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */
    #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 Macro has been consolidated for readability reasons. */
        uint8_t    ucStaticallyAllocated;         /*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */
    #endif

    #if( INCLUDE_xTaskAbortDelay == 1 )
        uint8_t ucDelayAborted;
    #endif

} tskTCB;

/* The old tskTCB name is maintained above then typedefed to the new TCB_t name
below to enable the use of older kernel aware debuggers. */
typedef tskTCB TCB_t;
pxTopOfStack 任务堆栈栈顶
xStateListItem 状态列表项
xEventListItem 事件列表项
uxPriority 任务优先级
*pxStack 任务栈起始地址
pcTaskName[16] 任务名称
*pxEndOfStack 任务堆栈栈底

状态列表项 :
每个任务都有4种状态,FreeRTOS 使用一种高效的数据结构双向链表保存任务的状态,Linux中也是。

事件列表项 :
信号量,消息队列,软件定时器等事件的管理

任务创建流程分析

任务创建流程如下图所示:
在这里插入图片描述
在分析FreeRTOS 任务创建源码之前,我们得先了解一下栈的不同类型。

栈的四种类型

不同的硬件平台stack的增长方式不同,总的来说是有4种增长方式:

满减栈、满增栈、空减栈、空增栈。

满减栈:栈指针指向最后压入栈的数据,数据入栈时,sp先减一再入栈。

满增栈:栈指针指向最后压入栈的数据,数据入栈时,sp先加一再入栈。

空减栈:栈指针指向下一个将要放入数据的位置,数据入栈时,先入栈sp再减一。

空增栈:栈指针指向下一个将要放入数据的位置,数据入栈时,先入栈sp再加一。

满栈进栈是先移动指针再存;满栈出栈是先出数据再移动指针;

空栈进栈先存再移动指针;空栈出栈先移动指针再取数据。

ARM M3,M4内核使用的是 满减栈

任务创建源码分析

我们在源码中直接使用注释分析:

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )

    BaseType_t xTaskCreate(    TaskFunction_t pxTaskCode,
                            const char * const pcName,        /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                            const configSTACK_DEPTH_TYPE usStackDepth,
                            void * const pvParameters,
                            UBaseType_t uxPriority,
                            TaskHandle_t * const pxCreatedTask )
    {
    TCB_t *pxNewTCB;
    BaseType_t xReturn;

        /* If the stack grows down then allocate the stack then the TCB so the stack
        does not grow into the TCB.  Likewise if the stack grows up then allocate
        the TCB then the stack. 
        根据自己的硬件平台堆栈增长方式来判断使用那一部分编译,
        堆栈增长方式:
        1、满减栈
        2、满增栈
        3、空减栈
        4、空增栈
        #define portSTACK_GROWTH            ( -1 )
        表示满减栈
        */
        #if( portSTACK_GROWTH > 0 )
        {
            /* Allocate space for the TCB.  Where the memory comes from depends on
            the implementation of the port malloc function and whether or not static
            allocation is being used.
            任务栈内存分配
             */
            pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );

            if( pxNewTCB != NULL )
            {
                /* Allocate space for the stack used by the task being created.
                The base of the stack memory stored in the TCB so the task can
                be deleted later if required. 
                任务控制块内存分配
                */
                pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */

                if( pxNewTCB->pxStack == NULL )
                {
                    /* Could not allocate the stack.  Delete the allocated TCB. */
                    vPortFree( pxNewTCB );
                    pxNewTCB = NULL;
                }
            }
        }
        #else 
        /* portSTACK_GROWTH */
        /*
        M3,M4中使用的下面这种
        
        #define portSTACK_TYPE    uint32_t
        typedef portSTACK_TYPE StackType_t;
        
        每个内存是4个字节(StackType_t),所以创建堆栈是按照字来分配的
        默认的是128 x 4 = 512字节;
        
        */
        {
        StackType_t *pxStack;

            /* Allocate space for the stack used by the task being created. */
            pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */

            if( pxStack != NULL )
            {
                /* 
                Allocate space for the TCB.
                根据TCB的大小还得分配空间 
                */
                pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e961 MISRA exception as the casts are only redundant for some paths. */

                if( pxNewTCB != NULL )
                {
                    /* Store the stack location in the TCB.
                    赋值栈地址
                     */
                    pxNewTCB->pxStack = pxStack;
                }
                else
                {
                    /* The stack cannot be used as the TCB was not created.  Free
                    it again. 
                    释放栈空间
                    */
                    vPortFree( pxStack );
                }
            }
            else
            {
                pxNewTCB = NULL;
            }
        }
        #endif /* portSTACK_GROWTH */

        if( pxNewTCB != NULL )
        {
            #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 Macro has been consolidated for readability reasons. */
            {
                /* Tasks can be created statically or dynamically, so note this
                task was created dynamically in case it is later deleted. */
                pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB;
            }
            #endif /* configSUPPORT_STATIC_ALLOCATION */
            /*
            新建任务初始化
            初始化任务控制块
            初始化任务堆栈
            */
            prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL );
            /*
            把任务添加到就绪列表中
            */
            prvAddNewTaskToReadyList( pxNewTCB );
            xReturn = pdPASS;
        }
        else
        {
            xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
        }

        return xReturn;
    }

#endif /* configSUPPORT_DYNAMIC_ALLOCATION */

任务删除流程分析

在这里插入图片描述

临界段

一种保护机制,代码的临界段也称为临界区,指处理时不可分割的代码区域,一旦这部分代码开始执行,则不允许任何中断打断。为确保临界段代码的执行不被中断,在进入临界段之前须关中断,而临界段代码执行完毕后,要立即打开中断。

在OS中,除了外部中断能将正在运行的代码打断,还有线程的调度——PendSV,系统产生 PendSV中断,在 PendSV Handler 里面实现线程的切换。我们要将这项东西屏蔽掉,保证当前只有一个线程在使用临界资源。

FreeRTOS对中断的开和关是通过操作 BASEPRI 寄存器来实现的,即大于等于 BASEPRI 的值的中断会被屏蔽,小于 BASEPRI 的值的中断则不会被屏蔽。这样子的好处就是用户可以设置 BASEPRI 的值来选择性的给一些非常紧急的中断留一条后路。

FreeRTOS 进入临界段 屏蔽中断:

/*-----------------------------------------------------------*/

portFORCE_INLINE static void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI;

    __asm volatile
    (
        "    mov %0, %1                                                \n" \
        "    msr basepri, %0                                            \n" \
        "    isb                                                        \n" \
        "    dsb                                                        \n" \
        :"=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory"
    );
}

FreeRTOS 退出临界段 打开中断:

*-----------------------------------------------------------*/

portFORCE_INLINE static void vPortSetBASEPRI( uint32_t ulNewMaskValue )
{
    __asm volatile
    (
        "    msr basepri, %0    " :: "r" ( ulNewMaskValue ) : "memory"
    );
}

任务删除源码分析

我们在源码中直接使用注释分析:

#if ( INCLUDE_vTaskDelete == 1 )

    void vTaskDelete( TaskHandle_t xTaskToDelete )
    {
    TCB_t *pxTCB;
        /*
        进入临界段
        */
        taskENTER_CRITICAL();
        {
            /* If null is passed in here then it is the calling task that is
            being deleted. 
            获取任务控制块 -----参数传入的是任务句柄
            判断是任务本身,还是其他任务
            */
            pxTCB = prvGetTCBFromHandle( xTaskToDelete );

            /* 
            Remove task from the ready list. 
            从就绪列表中移除
            */
            if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
            {
                taskRESET_READY_PRIORITY( pxTCB->uxPriority );
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }

            /* 
            Is the task waiting on an event also? 
            从事件列表中移除
            */
            if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
            {
                ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }

            /* Increment the uxTaskNumber also so kernel aware debuggers can
            detect that the task lists need re-generating.  This is done before
            portPRE_TASK_DELETE_HOOK() as in the Windows port that macro will
            not return. */
            uxTaskNumber++;
            /*
            如果是删除当前任务
            */
            if( pxTCB == pxCurrentTCB )
            {
                /* A task is deleting itself.  This cannot complete within the
                task itself, as a context switch to another task is required.
                Place the task in the termination list.  The idle task will
                check the termination list and free up any memory allocated by
                the scheduler for the TCB and stack of the deleted task.
                删除任务本身不能立即删除,在空闲任务中删除
                就把任务添加到等待删除的任务列表中
                 */
                vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) );

                /* Increment the ucTasksDeleted variable so the idle task knows
                there is a task that has been deleted and that it should therefore
                check the xTasksWaitingTermination list. 
                给空间任务一个标记
                */
                ++uxDeletedTasksWaitingCleanUp;

                /* The pre-delete hook is primarily for the Windows simulator,
                in which Windows specific clean up operations are performed,
                after which it is not possible to yield away from this task -
                hence xYieldPending is used to latch that a context switch is
                required.
                钩子函数----用户自己实现 
                */
                portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending );
            }
            else
            {
                --uxCurrentNumberOfTasks;
                prvDeleteTCB( pxTCB );

                /* Reset the next expected unblock time in case it referred to
                the task that has just been deleted. 
                复位任务锁定时间 ----时间片 操作系统会记录一个最新的时间,
                根据最新的时间,进行调度,所以删除任务后,要更新锁定时间
                */
                prvResetNextTaskUnblockTime();
            }
            
            traceTASK_DELETE( pxTCB );
        }
        /*退出临界段*/
        taskEXIT_CRITICAL();

        /* Force a reschedule if it is the currently running task that has just been deleted. 
        判断调度器是否开启
        */
        if( xSchedulerRunning != pdFALSE )
        {
            /*
            如果是删除任务本身,马上进行任务调度(释放CPU的使用权)
            */
            if( pxTCB == pxCurrentTCB )
            {
                configASSERT( uxSchedulerSuspended == 0 );
                portYIELD_WITHIN_API();
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
    }

#endif /* INCLUDE_vTaskDelete */

任务挂起

在这里插入图片描述
我们在源码中直接使用注释分析:

#if ( INCLUDE_vTaskSuspend == 1 )

    void vTaskSuspend( TaskHandle_t xTaskToSuspend )
    {
    TCB_t *pxTCB;
        /*
        进入临界段
        */
        taskENTER_CRITICAL();
        {
            /* If null is passed in here then it is the running task that is
            being suspended. 
            获取任务控制块
            */
            pxTCB = prvGetTCBFromHandle( xTaskToSuspend );

            traceTASK_SUSPEND( pxTCB );

            /* Remove task from the ready/delayed list and place in the
            suspended list. 
            从就绪列表中删除
            */
            if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
            {
                taskRESET_READY_PRIORITY( pxTCB->uxPriority );
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }

            /* Is the task waiting on an event also? 
            从事件列表中移除
            */
            if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
            {
                ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
            /*
            把任务添加到挂起列表中
            */
            vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) );

            #if( configUSE_TASK_NOTIFICATIONS == 1 )
            {
                if( pxTCB->ucNotifyState == taskWAITING_NOTIFICATION )
                {
                    /* The task was blocked to wait for a notification, but is
                    now suspended, so no notification was received. */
                    pxTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
                }
            }
            #endif
        }
        /*
        退出临界段
        */
        taskEXIT_CRITICAL();
        /*
        判断调度器是否开始
        看看是否有更高优先级的任务
        */
        if( xSchedulerRunning != pdFALSE )
        {
            /* Reset the next expected unblock time in case it referred to the
            task that is now in the Suspended state. */
            taskENTER_CRITICAL();
            {
                prvResetNextTaskUnblockTime();
            }
            taskEXIT_CRITICAL();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }

        if( pxTCB == pxCurrentTCB )
        {
            if( xSchedulerRunning != pdFALSE )
            {
                /* The current task has just been suspended. 
                直接进行任务调度,释放CPU
                */
                configASSERT( uxSchedulerSuspended == 0 );
                portYIELD_WITHIN_API();
            }
            else
            {
                /* The scheduler is not running, but the task that was pointed
                to by pxCurrentTCB has just been suspended and pxCurrentTCB
                must be adjusted to point to a different task. 
                调度器没有开启,pxCurrentTCB必须指向另外一个不同的task
                因为这个任务被挂起了
                */
                if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks )
                {
                    /* No other tasks are ready, so set pxCurrentTCB back to
                    NULL so when the next task is created pxCurrentTCB will
                    be set to point to it no matter what its relative priority
                    is. 
                    没有任务准备,把当前的任务控制块复制为NULL
                    */
                    pxCurrentTCB = NULL;
                }
                else
                {
                    /*手动进行调度,在就绪列表中找到优先级最高的任务*/
                    vTaskSwitchContext();
                }
            }
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }

#endif /* INCLUDE_vTaskSuspend */

任务恢复

在这里插入图片描述我们在源码中直接使用注释分析:

#if ( INCLUDE_vTaskSuspend == 1 )

    void vTaskResume( TaskHandle_t xTaskToResume )
    {
    /*获取要恢复的任务控制块*/
    TCB_t * const pxTCB = ( TCB_t * ) xTaskToResume;

        /* It does not make sense to resume the calling task. 
        检查*/
        configASSERT( xTaskToResume );

        /* The parameter cannot be NULL as it is impossible to resume the
        currently executing task. 
        恢复的话不能是NULL或当前的任务 */
        if( ( pxTCB != NULL ) && ( pxTCB != pxCurrentTCB ) )
        {
            /*进入临界段*/
            taskENTER_CRITICAL();
            {
                /*判断任务是否已经挂起*/
                if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE )
                {
                    traceTASK_RESUME( pxTCB );

                    /* The ready list can be accessed even if the scheduler is
                    suspended because this is inside a critical section. 
                    从挂起列表中移除
                    */
                    ( void ) uxListRemove(  &( pxTCB->xStateListItem ) );
                    /*添加到就绪列表*/
                    prvAddTaskToReadyList( pxTCB );

                    /* A higher priority task may have just been resumed. 
                    判断要恢复的任务优先级是否大于当前任务的优先级,
                    如果大于,释放CPU使用权,开始内核调度
                    */
                    if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
                    {
                        /* This yield may not cause the task just resumed to run,
                        but will leave the lists in the correct state for the
                        next yield. */
                        taskYIELD_IF_USING_PREEMPTION();
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            /*退出临界段*/
            taskEXIT_CRITICAL();
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }

#endif /* INCLUDE_vTaskSuspend */
相关文章
|
6月前
|
存储 算法 关系型数据库
实时计算 Flink版产品使用合集之在Flink Stream API中,可以在任务启动时初始化一些静态的参数并将其存储在内存中吗
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
127 4
|
11天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
39 2
|
1月前
|
存储 数据可视化 JavaScript
可视化集成API接口请求+变量绑定+源码输出
可视化集成API接口请求+变量绑定+源码输出
49 4
|
2月前
|
API Python
4. salt-api请求salt-minion执行任务 tornado超时报错
4. salt-api请求salt-minion执行任务 tornado超时报错
|
3月前
|
存储 Linux API
Linux源码阅读笔记08-进程调度API系统调用案例分析
Linux源码阅读笔记08-进程调度API系统调用案例分析
|
3月前
|
Linux API
Linux源码阅读笔记07-进程管理4大常用API函数
Linux源码阅读笔记07-进程管理4大常用API函数
|
3月前
|
JSON 算法 API
京东以图搜图功能API接口调用算法源码python
京东图搜接口是一款强大工具,通过上传图片即可搜索京东平台上的商品。适合电商平台、比价应用及需商品识别服务的场景。使用前需了解接口功能并注册开发者账号获取Key和Secret;准备好图片的Base64编码和AppKey;生成安全签名后,利用HTTP客户端发送POST请求至接口URL;最后解析JSON响应数据以获取商品信息。
|
3月前
|
JSON API 网络架构
Django 后端架构开发:DRF 高可用API设计与核心源码剖析
Django 后端架构开发:DRF 高可用API设计与核心源码剖析
72 0
|
4月前
|
自然语言处理 PyTorch API
`transformers`库是Hugging Face提供的一个开源库,它包含了大量的预训练模型和方便的API,用于自然语言处理(NLP)任务。在文本生成任务中,`transformers`库提供了许多预训练的生成模型,如GPT系列、T5、BART等。这些模型可以通过`pipeline()`函数方便地加载和使用,而`generate()`函数则是用于生成文本的核心函数。
`transformers`库是Hugging Face提供的一个开源库,它包含了大量的预训练模型和方便的API,用于自然语言处理(NLP)任务。在文本生成任务中,`transformers`库提供了许多预训练的生成模型,如GPT系列、T5、BART等。这些模型可以通过`pipeline()`函数方便地加载和使用,而`generate()`函数则是用于生成文本的核心函数。
|
5月前
|
DataWorks 安全 API
DataWorks产品使用合集之有api或者是sdk可以获取到 dataworks 的任务运行的结果吗
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
35 1
下一篇
无影云桌面