深入理解FreeRTOS_学习笔记(7(1)

三、任务通知源码分析

任务通知的创建就不用说了,任务被创建时则就便有了任务通知,而且FreeRTOS默认任务通知是开启的。

1.xTaskNotify函数

这个函数其实也是个宏定义,实际上调用的是xTaskGenericNotify这个函数

#define xTaskNotify( xTaskToNotify, ulValue, eAction ) \
 xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT\_INDEX\_TO\_NOTIFY ), ( ulValue ), ( eAction ), NULL )

具体的原码介绍,看中文注释

#if ( configUSE\_TASK\_NOTIFICATIONS == 1 )//配置任务通知相关宏

    BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify,
                                   UBaseType_t uxIndexToNotify,
                                   uint32\_t ulValue,
                                   eNotifyAction eAction,
                                   uint32\_t \* pulPreviousNotificationValue )
    {
        TCB_t \* pxTCB;				//指向哪个任务句柄
        BaseType_t xReturn = pdPASS;//返回值,用于判断是否设置通知成功
        uint8\_t ucOriginalNotifyState;//原始的任务状态

        configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES );
        configASSERT( xTaskToNotify );
        pxTCB = xTaskToNotify;//指向任务句柄

        taskENTER\_CRITICAL();//进入临界区
        {
            if( pulPreviousNotificationValue != NULL )//判断原始通知值是否存在
            {
                \*pulPreviousNotificationValue = pxTCB->ulNotifiedValue[ uxIndexToNotify ];
                //覆盖任务通知值
            }
			//ucOriginalNotifyState 获得任务的原始状态
            ucOriginalNotifyState = pxTCB->ucNotifyState[ uxIndexToNotify ];
			//任务的状态设置为接收通知状态
            pxTCB->ucNotifyState[ uxIndexToNotify ] = taskNOTIFICATION_RECEIVED;

            switch( eAction )
/\*eAction具有五个模式typedef enum
{
 eNoAction = 0, /\* 通知任务而不更新其通知值 
 eSetBits, /\* 在任务的通知值中设置位。 
 eIncrement, /\* 增加任务的通知值。
 eSetValueWithOverwrite, /\* 将任务的通知值设置为特定值,即使任务尚未读取以前的值。 
 eSetValueWithoutOverwrite /\* 如果任务已读取前一个值,则设置任务的通知值。
} eNotifyAction;
\*/
            {
                case eSetBits:
                    pxTCB->ulNotifiedValue[ uxIndexToNotify ] |= ulValue;
                    break;

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

                case eSetValueWithOverwrite:
                    pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue;
                    break;

                case eSetValueWithoutOverwrite:

                    if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED )
                    {
                        pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue;
                    }
                    else
                    {
                        /\* 无法将值写入任务 \*/
                        xReturn = pdFAIL;
                    }

                    break;

                case eNoAction:

                    /\* 正在通知任务,但未更新其通知值。\*/
                    break;

                default:

                    /\* 如果处理了所有枚举,则不应到达此处。通过测试编译器不能假定为常量的值来人为地强制断言。 \*/
                    configASSERT( xTickCount == ( TickType_t ) 0 );

                    break;
            }

            traceTASK\_NOTIFY( uxIndexToNotify );

            /\* 如果任务处于阻止状态,专门用于等待通知,则立即取消阻止。 \*/
            if( ucOriginalNotifyState == taskWAITING_NOTIFICATION )
            //如果原始的任务状态就是等待通知状态,就立刻从阻塞链表中移除加入就绪链表中
            {
                ( void ) uxListRemove( &( pxTCB->xStateListItem ) );
                prvAddTaskToReadyList( pxTCB );
                

                /\*该任务不应出现在事件列表中。 \*/
                configASSERT( listLIST\_ITEM\_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL );

                #if ( configUSE\_TICKLESS\_IDLE != 0 )
                    {
                        /\* 如果任务被阻止等待通知,则xNextTaskUnblockTime可能会设置为被阻止任务的超时时间。如果由于超时以外的原因取消阻止任务,xNextTaskUnblockTime通常保持不变,因为当勾号计数等于xNextTTaskUnblocktime时,它将自动重置为新值。然而,如果使用了无障碍空转,那么尽早进入睡眠模式可能更为重要——因此,请在此处重置xNextTaskUnblockTime,以确保其尽早更新。 \*/
                        prvResetNextTaskUnblockTime();
                    }
                #endif

                if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )
                {
                    /\* 通知的任务的优先级高于当前执行的任务,实现任务切换 \*/
                    taskYIELD\_IF\_USING\_PREEMPTION();
                }
                else
                {
                    mtCOVERAGE\_TEST\_MARKER();
                }
            }
            else
            {
                mtCOVERAGE\_TEST\_MARKER();
            }
        }
        taskEXIT\_CRITICAL();

        return xReturn;
    }

#endif 

其实也不难理解,前面的部分中通过switch case分支对任务的值进行了修改,之后就是对状态的判断,如果任务原始状态就是等待通知,此时发送了通知给他,则立刻将这个任务从阻塞态唤醒,并且修改它的任务状态。

2.xTaskNotifyWait函数

这个函数也是一个宏定义,实际调用的是xTaskGenericNotifyWait这个函数

#define xTaskNotifyWait( ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait ) \
 xTaskGenericNotifyWait( tskDEFAULT\_INDEX\_TO\_NOTIFY, ( ulBitsToClearOnEntry ), ( ulBitsToClearOnExit ), ( pulNotificationValue ), ( xTicksToWait ) )

xTaskGenericNotifyWait介绍一下它的参数:

uxIndexToWait:要等待的通知的索引,使用相同的索引与xTaskGenericNotify()函数发出的通知相对应;
ulBitsToClearOnEntry:在等待过程中要清除的通知位,在等待开始时使用,可以防止在等待之前已经发生的通知导致等待提前结束;
ulBitsToClearOnExit:在退出等待状态时要清除的通知位,使用该参数可以确保在通知发送时只提供一个等待任务许可。如果该参数是0,则预期任意通知都可以退出该函数;
pulNotificationValue:指向存储前一个通知值的指针,如果不使用,则可以将其设置为0;
xTicksToWait:等待任务通知的时间,如果等待超时,则函数返回pdFAIL。

以下为源码介绍:

#if ( configUSE\_TASK\_NOTIFICATIONS == 1 )

    BaseType_t xTaskGenericNotifyWait( UBaseType_t uxIndexToWait,
                                       uint32\_t ulBitsToClearOnEntry,
                                       uint32\_t ulBitsToClearOnExit,
                                       uint32\_t \* pulNotificationValue,
                                       TickType_t xTicksToWait )
    {
	    //判断是否接收通知成功
        BaseType_t xReturn;
		//防止数组越界
        configASSERT( uxIndexToWait < configTASK_NOTIFICATION_ARRAY_ENTRIES );
		//进入临界区
        taskENTER\_CRITICAL();
        {
            /\* 仅当通知尚未挂起时阻止\*/
            if( pxCurrentTCB->ucNotifyState[ uxIndexToWait ] != taskNOTIFICATION_RECEIVED )//当前任务状态如果不等于接收通知状态,也就是并未开始接收
            {
                /\* 清除任务通知值中的位,因为位可能由通知任务或中断设置。这可用于将值清除为零。\*/
                pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] &= ~ulBitsToClearOnEntry;

                /\*将此任务标记为正在等待通知 \*/
                pxCurrentTCB->ucNotifyState[ uxIndexToWait ] = taskWAITING_NOTIFICATION;
				//判断是否设置超时时间
                if( xTicksToWait > ( TickType_t ) 0 )
                {
                	//放入阻塞链表中
                    prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
                    traceTASK\_NOTIFY\_WAIT\_BLOCK( uxIndexToWait );
                             
                    portYIELD\_WITHIN\_API();
                }
                else
                {
                    mtCOVERAGE\_TEST\_MARKER();
                }
            }
            else
            {
                mtCOVERAGE\_TEST\_MARKER();
            }
        }
        taskEXIT\_CRITICAL();

        taskENTER\_CRITICAL();
        {
            traceTASK\_NOTIFY\_WAIT( uxIndexToWait );

            if( pulNotificationValue != NULL )//判断前一个通知值是否为空
            {
                /\* 覆盖当前通知值,该值可能已更改,也可能未更改 \*/
                \*pulNotificationValue = pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ];
            }

            /\* 如果设置了ucNotifyValue,则任务从未进入阻止状态(因为通知已挂起),或者由于通知而取消阻止任务。否则,由于超时,任务被取消阻止。 \*/
            if( pxCurrentTCB->ucNotifyState[ uxIndexToWait ] != taskNOTIFICATION_RECEIVED )//判断当前任务的状态是否为接收状态,如果不等于接收状态,则返回pdFALSE,表示并未收到通知值
            {
                /\* 未收到通知\*/
                xReturn = pdFALSE;
            }
            else
            {
                /\* 通知已挂起,或者在任务等待时收到通知。 \*/
                pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] &= ~ulBitsToClearOnExit;
                xReturn = pdTRUE;
            }
			//将状态设置为taskNOT\_WAITING\_NOTIFICATION
            pxCurrentTCB->ucNotifyState[ uxIndexToWait ] = taskNOT_WAITING_NOTIFICATION;
        }
        taskEXIT\_CRITICAL();

        return xReturn;
    }
#endif 

直接看代码注释,一定要接收/发送两个通知函数一起分析,这样理解起来轻松多了。

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新!!

片转存中…(img-n2HP3yVf-1715759053209)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值