根据个人的学习方向,学习FreeRTOS。由于野火小哥把FreeRTOS讲得比较含蓄,打算在本专栏尽量细化一点。作为个人笔记,仅供参考或查阅。

配套资料:FreeRTOS内核实现与应用开发实战指南、野火FreeRTOS配套视频源码、b站野火FreeRTOS视频。搭配来看更佳哟!!!

挂起任务

任务状态的迁移,FreeRTOS系统中的每一个任务都有多种运行状态,请看任务状态迁移图

1:创建任务→就绪态(Ready):任务创建完成后进入就绪态,表明任务已准备就绪,随时可以运行,只等待调度器进行调度。
2:就绪态→运行态(Running):发生任务切换时,就绪列表中最高优先级的任务被执行,从而进入运行态。
3:运行态→就绪态:有更高优先级任务创建或者恢复后,会发生任务调度,此刻就绪列表中最高优先级任务变为运行态,那么原先运行的任务由运行态变为就绪态,依然在就绪列表中,等待最高优先级的任务运行完毕继续运行原来的任务(此处可以看做是 CPU 使用权被更高优先级的任务抢占了)。
4:运行态→阻塞态(Blocked):正在运行的任务发生阻塞(挂起、延时、读信号量等待)时,该任务会从就绪列表中删除,任务状态由运行态变成阻塞态,然后发生任务切换,运行就绪列表中当前最高优先级任务。
5:阻塞态→就绪态:阻塞的任务被恢复后(任务恢复、延时时间超时、读信号量超时或读到信号量等),此时被恢复的任务会被加入就绪列表,从而由阻塞态变成就绪态;如果此时被恢复任务的优先级高于正在运行任务的优先级,则会发生任务切换,
将该任务将再次转换任务状态,由就绪态变成运行态。
6、7、8:就绪态、阻塞态、运行态→挂起态(Suspended):任务可以通过调用 vTaskSuspend() API 函数都可以将处于任何状态的任务挂起,被挂起的任务得不到CPU 的使用权,也不会参与调度,除非它从挂起态中解除。
9:挂起态→就绪态:把一个挂起状态的任务恢复的唯一途径就是调用 vTaskResume() 或 vTaskResumeFromISR() API函数,如果此时被恢复任务的优先级高于正在运行任务的优先级,则会发生任务切换,将该任务将再次转换任务状态,由就绪态变成运行态。

vTaskSuspend()挂起指定任务。被挂起的任务绝不会得到CPU的使用权,不管该任务具有什么优先级。相对于调度器而言是不可见的,除非它从挂起态中解除。

vTaskSuspend()如下

#if ( INCLUDE_vTaskSuspend == 1 )                        //FreeRTOSConfig.hvoid vTaskSuspend( TaskHandle_t xTaskToSuspend ){TCB_t *pxTCB;taskENTER_CRITICAL();{//#defineprvGetTCBFromHandle(pxHandle)(((pxHandle)==NULL)?(TCB_t*)pxCurrentTCB:(TCB_t*)(pxHandle))//如果pxTCB=prvGetTCBFromHandle(NULL);那么它就是正在运行的任务被挂起。//如果pxTCB=prvGetTCBFromHandle(xTaskToSuspend);相当于pxTCB=(TCB_t*)xTaskToSuspend;那么它就是指定任务被挂起。pxTCB = prvGetTCBFromHandle( xTaskToSuspend );traceTASK_SUSPEND( pxTCB );//从就绪/阻塞列表中删除任务,并在就绪/阻塞列表中把任务的优先级置0if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ){taskRESET_READY_PRIORITY( pxTCB->uxPriority );        //根据任务的优先级uxPriority将优先级位图uxTopReadyPriority的某个位置0}else{mtCOVERAGE_TEST_MARKER();}//listLIST_ITEM_CONTAINER返回链表所包含的节点//如果任务在等待事件,也从等待事件列表中移除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 isnow suspended, so no notification was received. */pxTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;}}#endif}taskEXIT_CRITICAL();if( xSchedulerRunning != pdFALSE )    //调度器正在运行{    //重置下一个任务的解除阻塞时间。重新计算一下还要多长时间执行下一个任务。//如果下个任务的解锁,刚好是被挂起的那个任务,那么变量NextTaskUnblockTime就不对了,所以要重新从延时列表中获取一下。//因为挂起的任务对调度器而言是不可见的,所以调度器是无法对挂起态的任务进行调度,所以要重新从延时列表中获取下一个要解除阻塞的任务。taskENTER_CRITICAL();{prvResetNextTaskUnblockTime();}taskEXIT_CRITICAL();}else{mtCOVERAGE_TEST_MARKER();}//如果挂起的是当前任务,并且调度器正在运行,则需要立即切换任务。不然系统的任务就错乱了,这是不允许的。if( pxTCB == pxCurrentTCB )                //如果挂起的是当前任务{if( xSchedulerRunning != pdFALSE )     //调度器正在运行{//当前的任务已经被挂起configASSERT( uxSchedulerSuspended == 0 );//立即切换任务portYIELD_WITHIN_API();}else{//调度器未运行,但pxCurrentTCB指向的任务刚刚被暂停,所以必须调整pxCurrentTCB以指向其他任务。//PRIVILEGED_DATAstaticvolatileUBaseType_tuxCurrentNumberOfTasks   =(UBaseType_t)0U;if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks )        //判断系统所有的任务是不是都被挂起了{//事实上并不会发生这种情况,因为空闲任务是不允许被挂起和阻塞的,必须保证系统中无论如何都有一个任务可以运行//没有其他任务准备就绪,因此将pxCurrentTCB设置回NULL,以便在创建下一个任务时pxCurrentTCB将被设置为指向它,实际上并不会执行到这里pxCurrentTCB = NULL;}else{vTaskSwitchContext();    //有其他任务,则切换到其他任务}}}else{mtCOVERAGE_TEST_MARKER();}}#endif /* INCLUDE_vTaskSuspend */

任务可以调用vTaskSuspend()这个函数来挂起任务自身,但是在挂起自身的时候会进行一次任务上下文切换,需要挂起自身就将xTaskToSuspend设置为NULL传递进来即可。
无论任务是什么状态都可以被挂起,只要调用了vTaskSuspend()这个函数就会挂起成功,不论是挂起其他任务还是挂起任务自身。
任务的挂起与解挂函数在很多时候都是很有用的,比如我们想暂停某个任务运行一段时间,但是我们又需要在其恢复的时候继续工作,那么删除任务是不可能的,因为删除了任务的话,
任务的所有的信息都是不可能恢复的了,删除是完完全全删除了,里面的资源都被系统释放掉,但是挂起任务就不会这样子,调用挂起任务函数,仅仅是将任务进入挂起态,其内部的资源都会保留下来,同时也不会参与系统中任务的调度,当调用恢复函数的时候,整个任务立即从挂起态进入就绪态,并且参与任务的调度,如果该任务的优先级是当前就绪态优先级最高的任务,那么立即会按照挂起前的任务状态继续执行该任务,从而达到我们需要的效果,注意,是继续执行,也就是说,挂起任务之前是什么状态,都会被系统保留下来,在恢复的瞬间,继续执行。

void  vTaskSuspendAll(void)  //将所有任务都挂起
{++uxSchedulerSuspended;
}

uxSchedulerSuspended用于记录调度器是否被挂起,默认初始值为pdFALSE,表明调度器是没被挂起的,每调用一次vTaskSuspendAll()函数就将变量加一,用于记录调用了多少次vTaskSuspendAll()函数。

挂起所有任务就是挂起任务调度器。调度器被挂起后则不能进行上下文切换,但是中断还是使能的。当调度器被挂起的时候,如果有中断需要进行上下文切换,那么这个任务将会被挂起,在调度器恢复之后才执行切换任务。

任务挂起vTaskSuspend()函数实例,上述函数官方都封装好的了,我们直接使用。

static  TaskHandle_t  LED_Task_Handle=NULL;/*LED任务句柄*/static  void  KEY_Task(void*parameter)
{while(1){if(Key_Scan( KEY1_GPIO_PORT , KEY1_GPIO_PIN ) == KEY_ON)    /*K1被按下*/{printf("挂起LED任务!\n");vTaskSuspend(LED_Task_Handle);                          /*挂起LED任务*/}vTaskDelay(20);                                             /*延时20个tick*/}
}

解挂任务

任务解挂就是让挂起的任务重新进入就绪状态,恢复的任务会保留挂起前的状态信息,在恢复的时候根据挂起时的状态继续运行。如果被恢复任务在所有就绪态任务中,处于最高优先级列表的第一位,那么系统将进行任务上下文的切换。

vTaskResume()函数如下

#if ( INCLUDE_vTaskSuspend == 1 )void vTaskResume( TaskHandle_t xTaskToResume ){TCB_t * const pxTCB = ( TCB_t * ) xTaskToResume;    //获取xTaskToResume对应的任务控制块//检查要恢复的任务是否存在configASSERT( xTaskToResume );//该任务不能为NULL,同时也无法恢复当前正在执行的任务,因为当前正在运行的任务不需要恢复,只能恢复处于挂起态的任务if( ( pxTCB != NULL ) && ( pxTCB != pxCurrentTCB ) ){taskENTER_CRITICAL();    //防止被打断{if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE )    //判断是否正在挂起态{traceTASK_RESUME( pxTCB );                      /*由于我们处于临界区,即使任务被挂起,我们也可以访问任务的状态列表。将要恢复的任务从挂起列表中删除*/( void ) uxListRemove(  &( pxTCB->xStateListItem ) );prvAddTaskToReadyList( pxTCB );                //将要恢复的任务添加到就绪列表中去//如果恢复的任务优先级比当前任务优先级高if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ){//进行任务的切换taskYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}taskEXIT_CRITICAL();}else{mtCOVERAGE_TEST_MARKER();}}#endif /* INCLUDE_vTaskSuspend */

vTaskResume()函数用于解除挂起的任务。无论任务在挂起时候调用过多少次这个vTaskSuspend()函数,也只需调用一次vTaskResume()函数即可将任务恢复运行。
当然,无论调用多少次的vTaskResume()函数,也只在任务是挂起态的时候才进行恢复。

任务解挂vTaskResume()函数实例,上述函数官方都封装好的了,我们直接使用。

static  TaskHandle_t  LED_Task_Handle=NULL;/*LED任务句柄*/static  void  KEY_Task(void*  parameter)
{while(1){if(Key_Scan( KEY2_GPIO_PORT , KEY2_GPIO_PIN ) == KEY_ON)    /*K2被按下*/{printf("恢复LED任务!\n");vTaskResume(LED_Task_Handle);                           /*恢复LED任务!*/}vTaskDelay(20);                                             /*延时20个tick*/}
}

xTaskResumeFromISR()与vTaskResume()一样都是用于恢复被挂起的任务。不一样的是xTaskResumeFromISR()专门用在中断服务程序中。
无论通过调用一次或多次vTaskSuspend()函数而被挂起的任务,也只需调用一次xTaskResumeFromISR()函数即可解挂。
要想使用该函数必须在FreeRTOSConfig.h中把INCLUDE_vTaskSuspend和INCLUDE_vTaskResumeFromISR都定义为1才有效。
任务还没有处于挂起态的时候,调用xTaskResumeFromISR()函数是没有任何意义的。

xTaskResumeFromISR()函数如下

#if ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) )BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ){BaseType_t xYieldRequired = pdFALSE;TCB_t * const pxTCB = ( TCB_t * ) xTaskToResume;UBaseType_t uxSavedInterruptStatus;                            //保存关闭中断的状态//检查要恢复的任务是否存在configASSERT( xTaskToResume );//检查中断优先级是否有效portASSERT_IF_INTERRUPT_PRIORITY_INVALID();uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();    //配置BASEPRI寄存器{if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE )           //判断任务是否真地被挂起了{traceTASK_RESUME_FROM_ISR( pxTCB );//调度器被挂起if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )    {//如果恢复的任务优先级比当前任务优先级高if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ){xYieldRequired = pdTRUE;    //切换任务请求标志置pdTRUE}else{mtCOVERAGE_TEST_MARKER();}/*由于我们处于临界区,即使任务被挂起,我们也可以访问任务的状态列表。将要恢复的任务从挂起列表中删除*/( void ) uxListRemove( &( pxTCB->xStateListItem ) );prvAddTaskToReadyList( pxTCB );    //将要恢复的任务添加到就绪列表中去}else    //调度器没有被挂起,任务插入到就绪列表 {vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) );}}else{mtCOVERAGE_TEST_MARKER();}}portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );    //清除BASEPRI寄存器的配置return xYieldRequired;    //返回切换任务请求标志,在外部选择是否进行任务切换}#endif /* ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) */

当函数的返回值为pdTRUE时:恢复运行的任务的优先级等于或高于正在运行的任务,表明在中断服务函数退出后必须进行一次上下文切换,使用portYIELD_FROM_ISR()进行上下文切换。
当函数的返回值为pdFALSE时:恢复运行的任务的优先级低于当前正在运行的任务,表明在中断服务函数退出后不需要进行上下文切换。

xTaskResumeFromISR()通常被认为是一个危险的函数,因为它的调用并非是固定的,中断可能随时来临。所以,xTaskResumeFromISR()不能用于任务和中断间的同步,如果中断恰巧在任务被挂起之前到达,这就会导致一次中断丢失(任务还没有挂起,调用xTaskResumeFromISR()函数是没有意义的,只能等下一次中断)。这种情况下,可以使用信号量或者任务通知来同步就可以避免这种情况。

xTaskResumeFromISR()实例如下

void  vAnExampleISR(void)
{BaseType_t  xYieldRequired;/*恢复被挂起的任务*/xYieldRequired = xTaskResumeFromISR(xHandle);if(xYieldRequired == pdTRUE){/*执行上下文切换,ISR返回的时候将运行另外一个任务*/portYIELD_FROM_ISR();}
}

xTaskResumeAll()函数如下

BaseType_t xTaskResumeAll( void )        //恢复调度器
{TCB_t *pxTCB = NULL;BaseType_t xAlreadyYielded = pdFALSE;/*如果uxSchedulerSuspended为0,则此函数与先前对vTaskSuspendAll()的调用不匹配,不需要调用xTaskResumeAll()恢复调度器。*/configASSERT( uxSchedulerSuspended );taskENTER_CRITICAL();{--uxSchedulerSuspended;if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )    //调度器没有被挂起,即恢复正常工作{if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U )    {//当待处理就绪列表不为空while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE ){//获取待处理就绪列表的第一个TCBpxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) );//移除第一个TCB( void ) uxListRemove( &( pxTCB->xEventListItem ) );( void ) uxListRemove( &( pxTCB->xStateListItem ) );prvAddTaskToReadyList( pxTCB );    //将TCB添加到就绪列表//如果恢复的TCB优先级高于等于当前任务的优先级if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ){xYieldPending = pdTRUE;    //切换任务请求标志置pdTRUE}else{mtCOVERAGE_TEST_MARKER();}}if( pxTCB != NULL ){/*在调度器被挂起时,任务被解除阻塞,这可能阻止了重新计算下一个解除阻塞时间。在这种情况下,重置下一个任务的解除阻塞时间*/prvResetNextTaskUnblockTime();    //重置下一个任务的解除阻塞时间。}//如果在调度器挂起这段时间产生滴答定时器的计时并且在这段时间有任务解除阻塞,由于调度器的挂起导致没法切换任务,当恢复调度器的时候应立即处理这些任务。//这样确保了滴答定时器的计数不会滑动,并且任何在延时的任务都会在正确的时间恢复{UBaseType_t uxPendedCounts = uxPendedTicks; /* Non-volatile copy. */if( uxPendedCounts > ( UBaseType_t ) 0U ){do{if( xTaskIncrementTick() != pdFALSE )    //查找是否有待进行切换的任务{xYieldPending = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}--uxPendedCounts;} while( uxPendedCounts > ( UBaseType_t ) 0U );uxPendedTicks = 0;}else{mtCOVERAGE_TEST_MARKER();}}if( xYieldPending != pdFALSE ){#if( configUSE_PREEMPTION != 0 ){xAlreadyYielded = pdTRUE;}#endiftaskYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}}else{mtCOVERAGE_TEST_MARKER();}}taskEXIT_CRITICAL();return xAlreadyYielded;
}

xTaskResumeAll函数的使用方法很简单,但是要注意,调用了多少次vTaskSuspendAll()函数就必须同样调用多少次xTaskResumeAll()函数。

本节所有函数都封装好的了,可以直接用。

vTaskSuspend()挂起指定任务。

vTaskSuspendAll()挂起任务调度器。

vTaskResume()解挂指定任务。

xTaskResumeAll()解挂任务调度器。

xTaskResumeFromISR()解挂指定任务,专门用在中断服务程序中。

FreeRTOS个人笔记-挂起/解挂任务相关推荐

  1. FreeRTOS笔记(四):任务创建/删除,挂起/解挂详解

    FreeRTOS笔记(四):任务创建/删除,挂起/解挂详解 在第二篇笔记中介绍了任务创建的API,并且简单使用了相关API,本文将详细介绍任务创建的过程. 一.任务创建 任务创建步骤为: 1.创建任务 ...

  2. (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记

    1.光盘的挂载与解挂 在Linux操作系统中,所有的存储设备都必须先挂载然后才能使用. 问题:为什么当我们直接访问/run/media/itheima/CentOS 7 x86_64就相当于访问光盘 ...

  3. Windows APC学习笔记(二)—— 挂入过程执行过程

    Windows APC学习笔记(二)-- 挂入过程&执行过程 基础知识 挂入过程 KeInitializeApc ApcStateIndex KiInsertQueueApc Alertabl ...

  4. linux 挂载u盘考试,Linux 挂载U盘,与解挂

    linux下u盘使用 一般情况下: 在root用户中 ,先创建目录,然后挂载就行. mkdir /usb 插入U盘 用/sbin/fdisk -l,应该能看U盘的磁盘名/dev/sdd1 mount ...

  5. 4-任务的挂起与解挂

    UCOSIII 任务的挂起与解挂 当某个任务在等待一些事情的时候,比如信号量.消息队列(以后会用到的)等,或者调用延时函数,该任务会进入挂起的状态,但是这是系统自动把任务挂起来的,不需要我们手动干预. ...

  6. linux--挂载,解挂

    挂载:在linux操作系统中, 挂载是指将一个设备(通常是存储设备)挂接到一个已存在的目录上. 我们要访问存储设备中的文件,必须将文件所在的分区挂载到一个已存在的目录上, 然后通过访问这个目录来访问存 ...

  7. Umount解挂不了的解决方法

    Umount解挂不了的解决方法 一:出现问题 二:解决方法 一:出现问题 在linux中卸载不了,出现这种报错 [root@dbserver /]# umount /dev/sdb6 umount: ...

  8. linux 下C语言编程(2)——进程的创建,挂起,解挂,进程间通信

    在 linux 下利用C语言实现进程的创建,挂起和解挂操作 #include <stdio.h> #include <sys/stat.h> #include <sys/ ...

  9. Linux磁盘挂载解挂硬盘

    1.模拟添加磁盘   2.查看磁盘情况是否挂载上 df -h 该命令会显示出挂载磁盘和挂载点,下图有2个硬盘挂载 /dev/sda3 和 /dev/sda1 查看磁盘挂载情况 lsblk 上图中, 磁 ...

最新文章

  1. docker 异常:“fork/exec /proc/self/exe: no such file”
  2. 2019 Multi-University Training Contest 1 - 1004 - Vacation - 二分 - 思维
  3. 子元素是字典列表转成字典
  4. 《深入剖析Tomcat》阅读(三)
  5. java string format s_JAVA字符串格式化-String.format()的使用
  6. 不到30的成本,还不赶紧造起来——盘点软著申请小知识
  7. 洛谷 深基 第1部分 语言入门 第4章 循环结构程序设计(2022.02.14)
  8. MySQL常用存储引擎
  9. oracle中schema是什么,ORACLE中的两个概念:user和schema的区别和联系
  10. sublime报错信息乱码_解决Sublime Text 3在GBK编码下的中文乱码问题
  11. 百度搜索大数据:“摆摊技巧”搜索热度暴涨655%;中国电信:将逐步关闭3G网络业务;IntelliJ IDEA新版发布|极客头条...
  12. NetFramework3.5 win10 64位 32位 安装资源——免费下载
  13. 假若明天来临——《AI.未来》读后感3900字
  14. 计算机家庭组改工作组,win7系统如何更改工作组
  15. 【海洋科学】高精度地形数据画出的水深图
  16. 计算机字号调整,解答如何调整电脑字体大小
  17. 90952-27-5,Thiol-PEG4-alcohol含有羟基和巯基的交联剂
  18. 建武28a对讲机最大距离_健伍TH-26A,TG-28A,TH-28A和TK208对讲机检修实例说明
  19. iconfont是什么?
  20. JavaScript怎么识别360浏览器?JS识别360急速模式方案,360流氓浏览器

热门文章

  1. java并发——构建高效且可伸缩的结果缓存
  2. 银行金融 智能业务对话机器人 全生命周期、功能实现及二次开发
  3. 读书百客:《无题》鉴赏
  4. 【技术流派】教你提高双目立体视觉系统的精度!
  5. MongoDB Linux简单操作命令 python操作 pymongo
  6. 2021年成人大专多长时间拿证?要几年?
  7. 2022-2028年全球与中国儿童服装行业市场需求预测分析
  8. 中国新生儿重症监护呼吸机行业市场供需与战略研究报告
  9. qq游戏和微信游戏是一个服务器吗,Dnf手游qq区微信区能一起玩吗
  10. 平板改文件服务器,平板改监控服务器