freertos内核走读2——task任务调度机制(四) notify机制

继续来走读task.c中的notify机制。

Notify的函数原型:

BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction )

如果要使能任务的notify机制,需要将configUSE_TASK_NOTIFICATIONS define为1.

熟悉freertos的朋友可能能够数出来,freertos下任务通讯的几种方式:queue,semaphores mutexes和event groups。其中semaphores mutexes都是基于队列的方式实现,notify机制和event groups最为类似,但是实现方式有较大差异。Notify机制是在每个任务中添加一个32位无符号字符标记,其他任务对该任务的通知。
回忆起任务上下文的结构TCB中的变量:

volatile uint32_t ulNotifiedValue;
volatile eNotifyValue eNotifyState;
typedef enum
{
    eNotWaitingNotification = 0,
    eWaitingNotification,
    eNotified
} eNotifyValue;

ulNotifiedValue就是那个32位无符号变量,而eNotifyState表示通知状态,eNotifyState表示一个简单的状态机,任务没有等待通知,任务等待通知,任务被通知。两个变量配合就实现了notify机制,因此任务的notify相对与其他消息机制更加高效,占用资源更少。

先回归到xTaskNotify原型解释。
入参1 xTaskToNotify,表示要通知的任务句柄;
入参2 ulValue,表示要设置的notify 值,这个值和期待的行为有关,按照不同的eAction对任务的ulNotifiedValue进行不同的操作。
入参3 eAction,表示插入值的行为,包括
eSetBits:设置ulNotifiedValue位域,每次返回pdPASS。
eIncrement:按照加行为更新ulNotifiedValue,每次都返回pdPASS;
eSetValueWithOverwrite,直接设置 ulNotifiedValue的值,并且不理会之前的通知更新的ulNotifiedValue是否被读走,直接进行覆盖。每次都返回pdPASS。
eSetValueWithoutOverwrite:和上面不同的是,如果任务有一个notify未被读走,怎不进行通知,而是返回pdFAIL。如果任务没有未处理的通知,则进行任务通知,更新ulNotifiedValue,返回pdPASS。
eNoAction:只进行时间通知,但是不更新ulNotifiedValue的值。总是返回pdPASS。

按照上述行为,可以调用xTaskNotify对任务进行多样的形式通知,例如实现类似计数信号量的功能,简单的消息通知等。
而notify等待任务(被通知的任务)调用xTaskNotifyWait() 和ulTaskNotifyTake()实现notify消息的获取,这个获取可以是阻塞的,也可以是非阻塞的。

函数定义:

#define xTaskNotify( xTaskToNotify, ulValue, eAction ) 
xTaskGenericNotify( ( xTaskToNotify ), ( ulValue ), ( eAction ), NULL )
#define xTaskNotifyAndQuery( xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue ) 
xTaskGenericNotify( ( xTaskToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotifyValue ) )
BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue ) PRIVILEGED_FUNCTION;

xTaskNotifyAndQuery函数比xTaskNotify多了一个参数pulPreviousNotifyValue,改指针用于返回进行通知之前的任务ulNotifiedValue值。

来看xTaskGenericNotify的实现。考虑下如何实现notify通知和什么时候完成任务的切换。

BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue )
    {
    TCB_t * pxTCB;
    eNotifyValue eOriginalNotifyState;
    BaseType_t xReturn = pdPASS;

        configASSERT( xTaskToNotify );
        pxTCB = ( TCB_t * ) xTaskToNotify;

        taskENTER_CRITICAL();
        {/*进入临界区*/
            if( pulPreviousNotificationValue != NULL )
            {/*如果pulPreviousNotificationValue 不为空,将被通知任务的ulNotifiedValue 返回*/
                *pulPreviousNotificationValue = pxTCB->ulNotifiedValue;
            }
            /*保存任务原始的notify state*/
            eOriginalNotifyState = pxTCB->eNotifyState;
            /*设置任务的notify state 为eNotified */
            pxTCB->eNotifyState = eNotified;

            switch( eAction )
            {
                case eSetBits   :/*位域设置,进行或操作*/
                    pxTCB->ulNotifiedValue |= ulValue;
                    break;

                case eIncrement : /*加操作,ulNotifiedValue进行加一
这时忽略了入参ulValue */
                    ( pxTCB->ulNotifiedValue )++;
                    break;

                case eSetValueWithOverwrite :/*不理会任务notify state,直
接赋值*/
                    pxTCB->ulNotifiedValue = ulValue;
                    break;

                case eSetValueWithoutOverwrite :
                    if( eOriginalNotifyState != eNotified )
                    {/*如果上次通知的事件被读走,state将不为eNotified ,
这时才设置ulNotifiedValue,否则返回失败 */
                        pxTCB->ulNotifiedValue = ulValue;
                    }
                    else
                    {
                        /* The value could not be written to the task. */
                        xReturn = pdFAIL;
                    }
                    break;

                case eNoAction:/*不更新任何值,但是eNotifyState 被设置eNotified */
                    /* The task is being notified without its notify value being
                    updated. */
                    break;
            }

            traceTASK_NOTIFY();

            /* If the task is in the blocked state specifically to wait for a
            notification then unblock it now. */
            if( eOriginalNotifyState == eWaitingNotification )
            {
/*如果被通知任务正处于等待通知状态,那么将等待任务从阻塞队列中删除,
然后添加到ready list中,如果你看过之前task的介绍,一定清楚
prvAddTaskToReadyList 的操作(即更新了就绪队列又更新了就绪任务标记)*/
                ( void ) uxListRemove( &( pxTCB->xGenericListItem ) );
                prvAddTaskToReadyList( pxTCB );

                /* The task should not have been on an event list. */
            /*等待任务notify的任务,必然不会等待queue等内核对象的资源,否则进入断言*/    configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL );

                #if( configUSE_TICKLESS_IDLE != 0 )
                {/*如果没有使用TICKLESS ,即没有开启空闲任务进入低功耗模式*/
                    /* If a task is blocked waiting for a notification then
                    xNextTaskUnblockTime might be set to the blocked task's time
                    out time.  If the task is unblocked for a reason other than
                    a timeout xNextTaskUnblockTime is normally left unchanged,
                    because it will automatically get reset to a new value when
                    the tick count equals xNextTaskUnblockTime.  However if
                    tickless idling is used it might be more important to enter
                    sleep mode at the earliest possible time - so reset
                    xNextTaskUnblockTime here to ensure it is updated at the
                    earliest possible time. */
                    /*因为删除的任务在阻塞队列里,因此要重新设置阻塞任务中,最快就要绪任务的tick点*/
                    prvResetNextTaskUnblockTime();
                }
                #endif

                if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )
                {/*如果被通知的任务优先级高于当前运行任务优先级则进行一次任务切换*/
                    /* The notified task has a priority above the currently
                    executing task so a yield is required. */
                    taskYIELD_IF_USING_PREEMPTION();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else
            {/*被通知任务的notify state不为eWaitingNotification ,表示任务没
有阻塞等待notify时间,则不进行上面的切换操作,被通知需要等待调度器调度到后取出通知
事件*/
                mtCOVERAGE_TEST_MARKER();
            }
        }
        taskEXIT_CRITICAL();

        return xReturn;
    }

这个函数还是蛮简单的。

另外一种情况是从中断回调里进行任务notify.

函数如下:

BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
#define xTaskNotifyFromISR( xTaskToNotify, ulValue, eAction, pxHigherPriorityTaskWoken ) 
xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( ulValue ), ( eAction ), NULL, ( pxHigherPriorityTaskWoken ) )
#define xTaskNotifyAndQueryFromISR( xTaskToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ) 
xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotificationValue ), ( pxHigherPriorityTaskWoken ) )

和上面的notify函数相比增加了一个参数pxHigherPriorityTaskWoken。
pxHigherPriorityTaskWoken用于返回这次任务notify操作是否触发了一个优先级更高的任务变为就绪状态(比较的对象是中断回调打断的任务优先级)。如果有的话,用户可以选择在中断结束后完成一次任务切换,使优先级高的任务抢占CPU。

来看xTaskGenericNotifyFromISR的实现:
和xTaskGenericNotify的主要区别是:进入临界区方式不同;对就绪任务的处理不同。这个函数也比较简单,和xTaskGenericNotify基本一致,可以大概走读下。

BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken )
    {
    TCB_t * pxTCB;
    eNotifyValue eOriginalNotifyState;
    BaseType_t xReturn = pdPASS;
    UBaseType_t uxSavedInterruptStatus;

        configASSERT( xTaskToNotify );

        /* RTOS ports that support interrupt nesting have the concept of a
        maximum system call (or maximum API call) interrupt priority.
        Interrupts that are above the maximum system call priority are keep
        permanently enabled, even when the RTOS kernel is in a critical section,
        but cannot make any calls to FreeRTOS API functions.  If configASSERT()
        is defined in FreeRTOSConfig.h then
        portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion
        failure if a FreeRTOS API function is called from an interrupt that has
        been assigned a priority above the configured maximum system call
        priority.  Only FreeRTOS functions that end in FromISR can be called
        from interrupts that have been assigned a priority at or (logically)
        below the maximum system call interrupt priority.  FreeRTOS maintains a
        separate interrupt safe API to ensure interrupt entry is as fast and as
        simple as possible.  More information (albeit Cortex-M specific) is
        provided on the following link:
        http://www.freertos.org/RTOS-Cortex-M3-M4.html */
        portASSERT_IF_INTERRUPT_PRIORITY_INVALID();

        pxTCB = ( TCB_t * ) xTaskToNotify;

        uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
        {
            if( pulPreviousNotificationValue != NULL )
            {
                *pulPreviousNotificationValue = pxTCB->ulNotifiedValue;
            }

            eOriginalNotifyState = pxTCB->eNotifyState;
            pxTCB->eNotifyState = eNotified;

            switch( eAction )
            {
                case eSetBits   :
                    pxTCB->ulNotifiedValue |= ulValue;
                    break;

                case eIncrement :
                    ( pxTCB->ulNotifiedValue )++;
                    break;

                case eSetValueWithOverwrite :
                    pxTCB->ulNotifiedValue = ulValue;
                    break;

                case eSetValueWithoutOverwrite :
                    if( eOriginalNotifyState != eNotified )
                    {
                        pxTCB->ulNotifiedValue = ulValue;
                    }
                    else
                    {
                        /* The value could not be written to the task. */
                        xReturn = pdFAIL;
                    }
                    break;

                case eNoAction :
                    /* The task is being notified without its notify value being
                    updated. */
                    break;
            }

            traceTASK_NOTIFY_FROM_ISR();

            /* If the task is in the blocked state specifically to wait for a
            notification then unblock it now. */
            if( eOriginalNotifyState == eWaitingNotification )
            {
                /* The task should not have been on an event list. */
                configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL );

                if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
                {
                    ( void ) uxListRemove( &( pxTCB->xGenericListItem ) );
                    prvAddTaskToReadyList( pxTCB );
                }
                else
                {
                    /* The delayed and ready lists cannot be accessed, so hold
                    this task pending until the scheduler is resumed. */
                    vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) );
                }

                if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )
                {
                    /* The notified task has a priority above the currently
                    executing task so a yield is required. */
                    if( pxHigherPriorityTaskWoken != NULL )
                    {
                        *pxHigherPriorityTaskWoken = pdTRUE;
                    }
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        }
        portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );

        return xReturn;
    }

任务notify等待

BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;

参数ulBitsToClearOnEntry:表示任务在获取自身的ulNotifiedValue值时,如果没有其他任务进行notify更新,则首先将ulNotifiedValue的某些位进行清除,相当于进行一个初始化操作。如果传入0xffffffffUL,就将ulNotifiedValue全部清理了,如果传入0,则不改变ulNotifiedValue。
参数:ulBitsToClearOnExit:当获取到任务自身的ulNotifiedValue值时,在退出函数前需要对ulNotifiedValue进行的某些位清除操作。
参数pulNotificationValue:用于获取任务自身的ulNotifiedValue。
xTicksToWait:阻塞等待的tick数,如果为0,表示非阻塞操作,为portMAX_DELAY表示永久阻塞直到读到notify事件。

xTaskNotifyWait的实现:

BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait )
    {
    TickType_t xTimeToWake;
    BaseType_t xReturn;

        taskENTER_CRITICAL();
        {/*进入临界区*/
            /* Only block if a notification is not already pending. */
            if( pxCurrentTCB->eNotifyState != eNotified )
            {/*如果任务没有未处理的notify事件则要进行阻塞操作。Notify state为eNotified 
表示有未被处理的notify事件*/
                /* Clear bits in the task's notification value as bits may get
                set by the notifying task or interrupt.  This can be used to
                clear the value to zero. */
                /*对应前面的参数说明,在获取ulNotifiedValue 前先将一些位清除*/
                pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnEntry;

                /* Mark this task as waiting for a notification. */
                /*将任务的notify状态修改为eWaitingNotification ,即等待notify*/
                pxCurrentTCB->eNotifyState = eWaitingNotification;

                if( xTicksToWait > ( TickType_t ) 0 )
                {
                    /* The task is going to block.  First it must be removed
                    from the    ready list. */
                    /*如果等待的tick大于零,则将当前任务从就绪队列里删除,然后更新就绪任务标记uxTopReadyPriority */
                    if( uxListRemove( &( pxCurrentTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 )
                    {
                        /* The current task must be in a ready list, so there is
                        no need to check, and the port reset macro can be called
                        directly. */
                        portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }

                    #if ( INCLUDE_vTaskSuspend == 1 )
                    {
                        if( xTicksToWait == portMAX_DELAY )
                        {/*如果阻塞等待的tick值为portMAX_DELAY,将任务添加到xSuspendedTaskList */
                            /* Add the task to the suspended task list instead
                            of a delayed task list to ensure the task is not
                            woken by a timing event.  It will block
                            indefinitely. */
                            vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xGenericListItem ) );
                        }
                        else
                        {/*如果不是最大等待时间,则添加到阻塞队列里
prvAddCurrentTaskToDelayedList 会判断是否发生了翻转,同时也会更新下次就绪任务tick点*/
                            /* Calculate the time at which the task should be
                            woken if no notification events occur.  This may
                            overflow but this doesn't matter, the scheduler will
                            handle it. */
                            xTimeToWake = xTickCount + xTicksToWait;
                            prvAddCurrentTaskToDelayedList( xTimeToWake );
                        }
                    }
                    #else /* INCLUDE_vTaskSuspend */
                    {/*如果没有使能暂停任务,则直接将任务添加到阻塞队列*/
                            /* Calculate the time at which the task should be
                            woken if the event does not occur.  This may
                            overflow but this doesn't matter, the scheduler will
                            handle it. */
                            xTimeToWake = xTickCount + xTicksToWait;
                            prvAddCurrentTaskToDelayedList( xTimeToWake );
                    }
                    #endif /* INCLUDE_vTaskSuspend */

                    traceTASK_NOTIFY_WAIT_BLOCK();

                    /* All ports are written to allow a yield in a critical
                    section (some will yield immediately, others wait until the
                    critical section exits) - but it is not something that
                    application code should ever do. */
                    /*因为当前任务已经添加到阻塞队列或者暂停队列,因此调用切换让出CPU
等待任务被唤醒后从该处向下继续执行*/
                    portYIELD_WITHIN_API();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        taskEXIT_CRITICAL();/*退出临界区*/
/*不管前面是怎么操作,任务能够运行到这里,表示当前任务的notify 状态为eNotified
或者阻塞操作超时了,任务被唤醒后执行到此处。
则进入临界区取走更新的 ulNotifiedValue */
        taskENTER_CRITICAL();
        {
            traceTASK_NOTIFY_WAIT();

            if( pulNotificationValue != NULL )
            {
                /* Output the current notification value, which may or may not
                have changed. */
/*如果需要返回ulNotifiedValue ,则返回该值*/
                *pulNotificationValue = pxCurrentTCB->ulNotifiedValue;
            }

            /* If eNotifyValue is set then either the task never entered the
            blocked state (because a notification was already pending) or the
            task unblocked because of a notification.  Otherwise the task
            unblocked because of a timeout. */
            if( pxCurrentTCB->eNotifyState == eWaitingNotification )
            {/*阻塞操作超时了,在这期间没有发生该任务的notify,返回pdFALSE */
                /* A notification was not received. */
                xReturn = pdFALSE;
            }
            else
            {
                /* A notification was already pending or a notification was
                received while the task was waiting. */
                /*其他情况下,表示成功获取了事件,在退出前将ulNotifiedValue某些bit清除*/
                pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnExit;
                xReturn = pdTRUE;
            }
        /*设置任务的NotifyState 为没有等待通知*/
            pxCurrentTCB->eNotifyState = eNotWaitingNotification;
        }
        taskEXIT_CRITICAL();

        return xReturn;
    }

xTaskNotifyGive
xTaskNotifyGive 是按照累加的方式通知其他任务,这种方式比xSemaphoreGive要快上不少。

#define xTaskNotifyGive( xTaskToNotify ) xTaskGenericNotify( ( xTaskToNotify ), ( 0 ), eIncrement, NULL )

vTaskNotifyGiveFromISR和xTaskNotifyGive实现不同。vTaskNotifyGiveFromISR可以返回pxHigherPriorityTaskWoken。以判断是否触发了优先级更高的任务进入就绪状态,在中断完成后完成一次切换。

void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPriorityTaskWoken )
    {
    TCB_t * pxTCB;
    eNotifyValue eOriginalNotifyState;
    UBaseType_t uxSavedInterruptStatus;

        configASSERT( xTaskToNotify );

        /* RTOS ports that support interrupt nesting have the concept of a
        maximum system call (or maximum API call) interrupt priority.
        Interrupts that are above the maximum system call priority are keep
        permanently enabled, even when the RTOS kernel is in a critical section,
        but cannot make any calls to FreeRTOS API functions.  If configASSERT()
        is defined in FreeRTOSConfig.h then
        portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion
        failure if a FreeRTOS API function is called from an interrupt that has
        been assigned a priority above the configured maximum system call
        priority.  Only FreeRTOS functions that end in FromISR can be called
        from interrupts that have been assigned a priority at or (logically)
        below the maximum system call interrupt priority.  FreeRTOS maintains a
        separate interrupt safe API to ensure interrupt entry is as fast and as
        simple as possible.  More information (albeit Cortex-M specific) is
        provided on the following link:
        http://www.freertos.org/RTOS-Cortex-M3-M4.html */
        portASSERT_IF_INTERRUPT_PRIORITY_INVALID();

        pxTCB = ( TCB_t * ) xTaskToNotify;

        uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
        {/*设置中断优先级标志,暂时屏蔽中断*/

            /*保存任务原始notify 状态*/
            eOriginalNotifyState = pxTCB->eNotifyState;
            /*设置任务notify状态为eNotified */
            pxTCB->eNotifyState = eNotified;

            /* 'Giving' is equivalent to incrementing a count in a counting
            semaphore. */
            /*give操作就相当于tasknotfiy的加操作*/
            ( pxTCB->ulNotifiedValue )++;

            traceTASK_NOTIFY_GIVE_FROM_ISR();

            /* If the task is in the blocked state specifically to wait for a
            notification then unblock it now. */
            if( eOriginalNotifyState == eWaitingNotification )
            {/*如果任务的原始状态为等待任务通知,表示任务正在阻塞等待notify*/
                /* The task should not have been on an event list. */
            /*这时候断言任务必须不是在等待其他的时间而进入了阻塞状态*/ configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL );

                if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
                {/*如果调度器没有暂停,则将任务从其他队列删除,并且添加到就绪队列
prvAddTaskToReadyList 也会更新任务就绪队列标记*/
                    ( void ) uxListRemove( &( pxTCB->xGenericListItem ) );
                    prvAddTaskToReadyList( pxTCB );
                }
                else
                {/*如果调度器暂停了,这时候将任务暂时添加到xPendingReadyList 
等待调度器再次运行时,再添加到就绪队列*/
                    /* The delayed and ready lists cannot be accessed, so hold
                    this task pending until the scheduler is resumed. */
                    vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) );
                }

                if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )
                {/*如果唤醒的任务优先级比当前运行任务优先级高,则设置
pxHigherPriorityTaskWoken ,中断可以选择在中断回调结束后完成任务切换*/
                    /* The notified task has a priority above the currently
                    executing task so a yield is required. */
                    if( pxHigherPriorityTaskWoken != NULL )
                    {
                        *pxHigherPriorityTaskWoken = pdTRUE;
                    }
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        }/*退出时清除中断回调*/
        portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
    }

ulTaskNotifyTake
参数xClearCountOnExit,表示在take操作结束后是否将任务的ulNotifiedValue清零,如果xClearCountOnExit为ture,则清零。否则每次清零时将ulNotifiedValue–。
xTicksToWait:表示阻塞操作等待时间。

uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait )
    {
    TickType_t xTimeToWake;
    uint32_t ulReturn;

        taskENTER_CRITICAL();
        {/*进入临界区*/
            /* Only block if the notification count is not already non-zero. */
            if( pxCurrentTCB->ulNotifiedValue == 0UL )
            {/*如果ulNotifiedValue 那么久必须进行阻塞操作,否则,直接take and return*/
                /* Mark this task as waiting for a notification. */
                /*设置notify 状态为等待notify事件*/
                pxCurrentTCB->eNotifyState = eWaitingNotification;

                if( xTicksToWait > ( TickType_t ) 0 )
                {
/*如果等待tick大于0,则将任务从就绪队列里删除,并且清除就绪任务标记uxTopReadyPriority */
                    /* The task is going to block.  First it must be removed
                    from the ready list. */
                    if( uxListRemove( &( pxCurrentTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 )
                    {
                        /* The current task must be in a ready list, so there is
                        no need to check, and the port reset macro can be called
                        directly. */
                        portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority );
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }

                    #if ( INCLUDE_vTaskSuspend == 1 )
                    {/*如果使能了task suspend选项,当任务选择永久阻塞时将任务添加到xSuspendedTaskList */
                        if( xTicksToWait == portMAX_DELAY )
                        {
                            /* Add the task to the suspended task list instead
                            of a delayed task list to ensure the task is not
                            woken by a timing event.  It will block
                            indefinitely. */
                            vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xGenericListItem ) );
                        }
                        else
                        {
                            /* Calculate the time at which the task should be
                            woken if no notification events occur.  This may
                            overflow but this doesn't matter, the scheduler will
                            handle it. */
                            xTimeToWake = xTickCount + xTicksToWait;
                            prvAddCurrentTaskToDelayedList( xTimeToWake );
                        }
                    }
                    #else /* INCLUDE_vTaskSuspend */
                    {/*如果没有使能Task Suspend,则统一添加到延时队列里,
并且会更新系统下次就绪任务tick点*/
                            /* Calculate the time at which the task should be
                            woken if the event does not occur.  This may
                            overflow but this doesn't matter, the scheduler will
                            handle it. */
                            xTimeToWake = xTickCount + xTicksToWait;
                            prvAddCurrentTaskToDelayedList( xTimeToWake );
                    }
                    #endif /* INCLUDE_vTaskSuspend */

                    traceTASK_NOTIFY_TAKE_BLOCK();

                    /* All ports are written to allow a yield in a critical
                    section (some will yield immediately, others wait until the
                    critical section exits) - but it is not something that
                    application code should ever do. */
                    /*因为当前任务已经从就绪状态变为阻塞或者suspend状态,那么就
让出cpu,等待唤醒或者超时后,从此处继续往下执行,而且所有的切
换操作必须支持在临界区内进行切换*/
                    portYIELD_WITHIN_API();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        taskEXIT_CRITICAL();
        /*程序执行到这里有一下几种情况:1. ulNotifiedValue一开始不为0,直接执行到这里;
2. ulNotifiedValue一开始为0,任务进入阻塞,然后期间被别的任务或者中断回调使用give操作唤醒。
3. 和情况2不同的时,没有第三者在等待期间唤醒任务,任务等待超时执行到此处*/
        taskENTER_CRITICAL();
        {
            traceTASK_NOTIFY_TAKE();
            ulReturn = pxCurrentTCB->ulNotifiedValue;

            if( ulReturn != 0UL )
            {
/*如果ulNotifiedValue 不为0,排除情况3*/
                if( xClearCountOnExit != pdFALSE )
                {/*如果使能了xClearCountOnExit ,则退出前清除ulNotifiedValue */
                    pxCurrentTCB->ulNotifiedValue = 0UL;
                }
                else
                {
                /*如果没有使能了xClearCountOnExit ,则退出前对ulNotifiedValue进行自减 */

                    ( pxCurrentTCB->ulNotifiedValue )--;
                }
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
            /*将任务的notify state设置为没有在等待通知*/
            pxCurrentTCB->eNotifyState = eNotWaitingNotification;
        }
        taskEXIT_CRITICAL();
        /*如果是情形3这里返回的就是0,也就是flase*/
        return ulReturn;
    }

xTaskNotifyStateClear
这个函数比较简单,就是进入临界区清除任务的notify state,但是不会对ulNotifiedValue进行任何操作。

BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask )
    {
    TCB_t *pxTCB;
    BaseType_t xReturn;

        pxTCB = ( TCB_t * ) xTask;

        /* If null is passed in here then it is the calling task that is having
        its notification state cleared. */
        pxTCB = prvGetTCBFromHandle( pxTCB );

        taskENTER_CRITICAL();
        {
            if( pxTCB->eNotifyState == eNotified )
            {
                pxTCB->eNotifyState = eNotWaitingNotification;
                xReturn = pdPASS;
            }
            else
            {
                xReturn = pdFAIL;
            }
        }
        taskEXIT_CRITICAL();

        return xReturn;
    }

Notify的机制比较简单,占用的资源也比较少,但是只能完成点到点的消息通知。但是在大多数情况下,其较小的开销,更高的效率是替代mutex或者semaphores的良好机制。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值