转载自 https://blog.csdn.net/zhoutaopower/article/details/107146764

前面有了创建任务、启动调度器、任务控制,接下来便开始分析一个 Tick 到来之后,FreeRTOS 即将有什么行为;

在启动调度器的时候,就已经配置好了 SysTick,它作为 OS 的心跳,每隔一个固定周期来一次 SysTick 中断,来驱动 OS 做事(任务调度);

以 STM32 为例,定义的 configTICK_RATE_HZ 为 1000,由《FreeRTOS --(9)任务管理之启动调度器》得知,系统节拍时钟周期为1ms;

不同的处理器结构可能有所区别,所以他是需要移植的部分,在 port.c 中 xPortSysTickHandler:

  1. void xPortSysTickHandler( void )
  2. {
  3. /* The SysTick runs at the lowest interrupt priority, so when this interrupt
  4. executes all interrupts must be unmasked. There is therefore no need to
  5. save and then restore the interrupt mask value as its value is already
  6. known - therefore the slightly faster vPortRaiseBASEPRI() function is used
  7. in place of portSET_INTERRUPT_MASK_FROM_ISR(). */
  8. vPortRaiseBASEPRI();
  9. {
  10. /* Increment the RTOS tick. */
  11. /* 如果返回值标记了任务切换,即有优先级高的任务 */
  12. if( xTaskIncrementTick() != pdFALSE )
  13. {
  14. /* A context switch is required. Context switching is performed in
  15. the PendSV interrupt. Pend the PendSV interrupt. */
  16. /* 设置PendSV中断位 */
  17. portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
  18. }
  19. }
  20. vPortClearBASEPRIFromISR();
  21. }

开头调用 vPortRaiseBASEPRI();结尾调用 vPortClearBASEPRIFromISR(); 是为了创造临界区;

调用了 xTaskIncrementTick;如果返回 pdTRUE 则代表要进行任务切换,那么就手动拉起 PendSV;否则不进行上下文切换;

接下来看下 xTaskIncrementTick 做了什么,大概猜测是选最高优先级的,并且在 Ready 链表的任务投入运行:

  1. BaseType_t xTaskIncrementTick( void )
  2. {
  3. TCB_t * pxTCB;
  4. TickType_t xItemValue;
  5. BaseType_t xSwitchRequired = pdFALSE;
  6.  
  7. /* Called by the portable layer each time a tick interrupt occurs.
  8. Increments the tick then checks to see if the new tick value will cause any
  9. tasks to be unblocked. */
  10. traceTASK_INCREMENT_TICK( xTickCount );
  11. if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
  12. {
  13. /* Minor optimisation. The tick count cannot change in this
  14. block. */
  15. const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1;
  16.  
  17. /* Increment the RTOS tick, switching the delayed and overflowed
  18. delayed lists if it wraps to 0. */
  19. xTickCount = xConstTickCount;
  20.  
  21. if( xConstTickCount == ( TickType_t ) 0U ) /*lint !e774 'if' does not always evaluate to false as it is looking for an overflow. */
  22. {
  23. taskSWITCH_DELAYED_LISTS();
  24. }
  25. else
  26. {
  27. mtCOVERAGE_TEST_MARKER();
  28. }
  29.  
  30. /* See if this tick has made a timeout expire. Tasks are stored in
  31. the queue in the order of their wake time - meaning once one task
  32. has been found whose block time has not expired there is no need to
  33. look any further down the list. */
  34. if( xConstTickCount >= xNextTaskUnblockTime )
  35. {
  36. for( ;; )
  37. {
  38. if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
  39. {
  40. /* The delayed list is empty. Set xNextTaskUnblockTime
  41. to the maximum possible value so it is extremely
  42. unlikely that the
  43. if( xTickCount >= xNextTaskUnblockTime ) test will pass
  44. next time through. */
  45. xNextTaskUnblockTime = portMAX_DELAY; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
  46. break;
  47. }
  48. else
  49. {
  50. /* The delayed list is not empty, get the value of the
  51. item at the head of the delayed list. This is the time
  52. at which the task at the head of the delayed list must
  53. be removed from the Blocked state. */
  54. pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */
  55. xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );
  56.  
  57. if( xConstTickCount < xItemValue )
  58. {
  59. /* It is not time to unblock this item yet, but the
  60. item value is the time at which the task at the head
  61. of the blocked list must be removed from the Blocked
  62. state - so record the item value in
  63. xNextTaskUnblockTime. */
  64. xNextTaskUnblockTime = xItemValue;
  65. break; /*lint !e9011 Code structure here is deedmed easier to understand with multiple breaks. */
  66. }
  67. else
  68. {
  69. mtCOVERAGE_TEST_MARKER();
  70. }
  71.  
  72. /* It is time to remove the item from the Blocked state. */
  73. ( void ) uxListRemove( &( pxTCB->xStateListItem ) );
  74.  
  75. /* Is the task waiting on an event also? If so remove
  76. it from the event list. */
  77. if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
  78. {
  79. ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
  80. }
  81. else
  82. {
  83. mtCOVERAGE_TEST_MARKER();
  84. }
  85.  
  86. /* Place the unblocked task into the appropriate ready
  87. list. */
  88. prvAddTaskToReadyList( pxTCB );
  89.  
  90. /* A task being unblocked cannot cause an immediate
  91. context switch if preemption is turned off. */
  92. #if ( configUSE_PREEMPTION == 1 )
  93. {
  94. /* Preemption is on, but a context switch should
  95. only be performed if the unblocked task has a
  96. priority that is equal to or higher than the
  97. currently executing task. */
  98. if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
  99. {
  100. xSwitchRequired = pdTRUE;
  101. }
  102. else
  103. {
  104. mtCOVERAGE_TEST_MARKER();
  105. }
  106. }
  107. #endif /* configUSE_PREEMPTION */
  108. }
  109. }
  110. }
  111.  
  112. /* Tasks of equal priority to the currently running task will share
  113. processing time (time slice) if preemption is on, and the application
  114. writer has not explicitly turned time slicing off. */
  115. #if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) )
  116. {
  117. if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 )
  118. {
  119. xSwitchRequired = pdTRUE;
  120. }
  121. else
  122. {
  123. mtCOVERAGE_TEST_MARKER();
  124. }
  125. }
  126. #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */
  127.  
  128. #if ( configUSE_TICK_HOOK == 1 )
  129. {
  130. /* Guard against the tick hook being called when the pended tick
  131. count is being unwound (when the scheduler is being unlocked). */
  132. if( xPendedTicks == ( TickType_t ) 0 )
  133. {
  134. vApplicationTickHook();
  135. }
  136. else
  137. {
  138. mtCOVERAGE_TEST_MARKER();
  139. }
  140. }
  141. #endif /* configUSE_TICK_HOOK */
  142.  
  143. #if ( configUSE_PREEMPTION == 1 )
  144. {
  145. if( xYieldPending != pdFALSE )
  146. {
  147. xSwitchRequired = pdTRUE;
  148. }
  149. else
  150. {
  151. mtCOVERAGE_TEST_MARKER();
  152. }
  153. }
  154. #endif /* configUSE_PREEMPTION */
  155. }
  156. else
  157. {
  158. ++xPendedTicks;
  159.  
  160. /* The tick hook gets called at regular intervals, even if the
  161. scheduler is locked. */
  162. #if ( configUSE_TICK_HOOK == 1 )
  163. {
  164. vApplicationTickHook();
  165. }
  166. #endif
  167. }
  168.  
  169. return xSwitchRequired;
  170. }

这个函数有点长,但是其实思路是非常清晰易懂的:

1、因为 FreeRTOS 支持挂起调度器,也就是调用 vTaskSuspendAll 后,RTOS 在每个 Tick 来临的时候,不在调度任务进行上下文切换;所以,每次进入 xTaskIncrementTick 的时候,要判断调度器是否被挂起;

2、如果允许调度,首先增加当前的计数器的计数:xTickCount;

3、增加完 xTickCount 后,判断计数器是否溢出,如果溢出了,那么调用 taskSWITCH_DELAYED_LISTS 来交换 pxDelayedTaskList 和 pxOverflowDelayedTaskList (为了解决xTickCount溢出问题,FreeRTOS使用了两个延时列表:xDelayedTaskList1 和 xDelayedTaskList2。并使用延时列表指针pxDelayedTaskList和溢出延时列表指针pxOverflowDelayedTaskList分别指向上面的延时列表1和延时列表2(在创建任务时将延时列表指针指向延时列表)。这两个延时列表指针变量和两个延时列表变量都是在tasks.c中定义的静态局部变量)

  1. /* pxDelayedTaskList and pxOverflowDelayedTaskList are switched when the tick
  2. count overflows. */
  3. #define taskSWITCH_DELAYED_LISTS() \
  4. { \
  5. List_t *pxTemp; \
  6. \
  7. /* The delayed tasks list should be empty when the lists are switched. */ \
  8. configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) ); \
  9. \
  10. pxTemp = pxDelayedTaskList; \
  11. pxDelayedTaskList = pxOverflowDelayedTaskList; \
  12. pxOverflowDelayedTaskList = pxTemp; \
  13. xNumOfOverflows++; \
  14. prvResetNextTaskUnblockTime(); \
  15. }

这两个链表专门为了处理计数器溢出而存在;一旦溢出,就交换,OS 始终取的是 pxDelayedTaskList 中的 Delay Task,在挂接任务的时候,判断时钟计数器,看是否需要往 pxOverflowDelayedTaskList 上面挂;

4、对比当前的时间 xConstTickCount 和下一个阻塞在时间上的任务的时间 xNextTaskUnblockTime 大小,查看阻塞时间是否到期,xNextTaskUnblockTime 是一个全局变量,记录着下一个最近的任务阻塞时间;

5、如果阻塞时间到期,那么首先判断当前的 Delay 链表是否为空,如果为空,则说明没有阻塞在时间上的任务,将 xNextTaskUnblockTime 赋值为最大 portMAX_DELAY,直接退出;

6、如果阻塞时间到期,而且 pxDelayedTaskList 链表不为空,那么取出 pxDelayedTaskList 链表的第一个元素(注意,往 pxDelayedTaskList 链表中插入 Item 的时候,是用 vListInsert ,根据唤醒时间有序插入的,即前面放置的是 Delay 时间最小的,后面是 Delay 大的)的时间,和当前的时间 xConstTickCount 进行比对,看看是否超期,如果没有超期,那么将其更新到下一个唤醒时间 xNextTaskUnblockTime 中,退出;如果到期,那么将其从 pxDelayedTaskList 链表中移除(如果在等 Event 也同时从 Event 中移除),将其添加到 ReadyList(prvAddTaskToReadyList),

  1. /*
  2. * Place the task represented by pxTCB into the appropriate ready list for
  3. * the task. It is inserted at the end of the list.
  4. */
  5. #define prvAddTaskToReadyList( pxTCB ) \
  6. traceMOVED_TASK_TO_READY_STATE( pxTCB ); \
  7. taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \
  8. vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \
  9. tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )

7、如果支持抢占的话(不支持抢占的 RTOS 是没有灵魂的),就要判断解除阻塞的任务和当前的任务的优先级,哪个更高,如果高于当前的任务优先级,那么 xSwitchRequired 设置为 pdTRUE,表示要进行一次上下文切换;

8、循环步骤 4 到 7,也就是对 pxDelayedTaskList  链表中的元素进行遍历,直到 pxDelayedTaskList 为空,或者有元素的运行时间还未到,还需要继续阻塞;

9、此刻,该到期的任务,已经全部从 pxDelayedTaskList  链表移动到了 pxReadyTasksLists 中,对应优先级的地方;

10、如果定义了抢占(configUSE_PREEMPTION),同时也定义了同一个优先级轮转调度(configUSE_TIME_SLICING) 的话呢(普通情况下,这两个都需要定义,不然没有灵魂),只要当前的任务所在的 pxReadyTasksLists 链表中,包含不止一个待运行的任务,就要去轮转调度另一个任务执行;所以 xSwitchRequired 设置为  pdTRUE;

11、如果应用层定义了 configUSE_TICK_HOOK,那么会调用 vApplicationTickHook 钩子;

12、如果定义了抢占(configUSE_PREEMPTION),而且 xYieldPending 也是 pdTRUE 的时候,也会设置 xSwitchRequired 设置为  pdTRUE,强制去进行上下文切换,

xYieldPending 这个变量什么时候会被设置称为 pdTRUE?

对于队列以及使用队列机制的信号量、互斥量等,在中断服务程序中调用了这些API函数,将任务从阻塞中解除,则需要调用函数xTaskRemoveFromEventList()将任务的事件列表项从事件列表中移除。在移除事件列表项的过程中,会判断解除的任务优先级是否大于当前任务的优先级,如果解除的任务优先级更高,会将变量xYieldPending设置为pdTRUE。在下一次系统节拍中断服务函数中,触发一次任务切换;

  1. if(pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority)
  2. {
  3. /*任务具有更高的优先级,返回pdTRUE。告诉调用这个函数的任务,它需要强制切换上下文。*/
  4. xReturn= pdTRUE;
  5.  
  6. /*带中断保护的API函数的都会有一个参数参数"xHigherPriorityTaskWoken",如果用户没有使用这个参数,这里设置任务切换标志。在下个系统中断服务例程中,会检查xYieldPending的值,如果为pdTRUE则会触发一次上下文切换。*/
  7. xYieldPending= pdTRUE;
  8. }

FreeRTOS --(11)任务管理之系统节拍的更多相关文章

  1. (41)freeRTOS之任务管理

    1. 简介: 在 FreeRTOS 中没有线程和进程的区别,只有一个被翻译成任务的程序,相当于进程的概念,拥有独立的栈空间. 对于实时性,可以分为 软实时.硬实时:桌面电脑的输入处理可以看做是软实时, ...

  2. Go语言学习之11 日志收集系统kafka库实战

    本节主要内容: 1. 日志收集系统设计2. 日志客户端开发 1. 项目背景    a. 每个系统都有日志,当系统出现问题时,需要通过日志解决问题    b. 当系统机器比较少时,登陆到服务器上查看即可 ...

  3. VMware 11 安装苹果系统

    没事研究了一下虚拟机安装苹果系统 1.下载需要的软件- F, c1 X: e- o1 }& V/ o9 J        1.1 VMware 11 下载和安装* P( R; O6 v1 N! ...

  4. 11、 机器学习系统的设计(Machine Learning System Design)

    11.1 首先要做什么 在接下来的视频中,我将谈到机器学习系统的设计.这些视频将谈及在设计复杂的机器学习系统时,你将遇到的主要问题.同时我们会试着给出一些关于如何巧妙构建一个复杂的机器学习系统的建议. ...

  5. C++11 正则表达式——实例系统(转载)

    一.用正则表达式判断邮箱格式是否正确 1 #include <regex> #include <iostream> #include <string> bool i ...

  6. Linux就该这么学--命令集合11(配置系统相关信息)

    1.配置主机名称: 查看主机名: hostname 修改主机名: vim /etc/hostname 2.配置网卡信息: 在红帽RHEL6系统中网卡配置文件的前缀为“ifcfg-eth”,第一块即为“ ...

  7. 10.11 android输入系统_补充知识_activity_window_decor_view关系

    android里:1个application, 有1个或多个activity(比如支付宝有:首页.财富.口碑.朋友.我的,这些就是activity)1个activity, 有1个window(每个ac ...

  8. e3mall商城总结11之sso系统的分析、应用以及解决ajax跨域问题

    说在前面的话 一.sso系统分析 什么是sso系统 SSO英文全称Single Sign On,单点登录.SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统.它包括可以将这次 ...

  9. FreeRTOS相关转载-(朱工的专栏)

    FreeRTOS系列第1篇---为什么选择FreeRTOS? 1.为什么学习RTOS? 作为基于ARM7.Cortex-M3硬件开发的嵌入式工程师,我一直反对使用RTOS.不仅因为不恰当的使用RTOS ...

随机推荐

  1. Java 建造者Builder

    import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; import java.u ...

  2. ConcurrentHashMap 的并发度是什么 ?

    ConcurrentHashMap 的并发度就是 segment 的大小,默认为 16,这意味着最多同时可以有 16 条线程操作 ConcurrentHashMap,这也是ConcurrentHash ...

  3. vue集成webpack,遭遇 SyntaxError: Unknown word

    这个错误根本和我的项目八竿子打不着,错误原因是配置了 css 的rule,将 这个rule注释掉,正常运行没有问题, 可是我却有强迫症,既然处理 node_modules 文件里才出现的错误,那么我就 ...

  4. (转载) MOS管区分NP沟道

    三极管是流控型器件,MOS管是压控型器件,两者存在相似之处.三极管机可能经常用,但MOS管你用的可能较少.对于MOS管先抛出几个问题: 如何区分P-MOS和N-MOS:   如何区分MOS的G.D.S ...

  5. Java 中,受检查异常 和 不受检查异常的区别?

    受检查异常编译器在编译期间检查.对于这种异常,方法强制处理或者通过 throws 子句声明.其中一种情况是 Exception 的子类但不是 RuntimeException 的子类.非受检查是 Ru ...

  6. ACM - 动态规划 - UVA 1347 Tour

    UVA 1347 Tour 题解 题目大意:有 \(n\) 个点,给出点的 \(x\).\(y\) 坐标.找出一条经过所有点一次的回路,从最左边的点出发,严格向右走,到达最右点再严格向左,回到最左点. ...

  7. .NET 6学习笔记(3)——在Windows Service中托管ASP.NET Core并指定端口

    在上一篇<.NET 6学习笔记(2)--通过Worker Service创建Windows Service>中,我们讨论了.NET Core 3.1或更新版本如何创建Windows Ser ...

  8. MOS管防反接电路设计

    转自嵌入式单片机之家公众号 问题的提出 电源反接,会给电路造成损坏,不过,电源反接是不可避免的.所以,我们就需要给电路中加入保护电路,达到即使接反电源,也不会损坏的目的 01二极管防反接 通常情况下直 ...

  9. 用Canvas画一棵二叉树

    笔墨伺候 var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); // 然后便可以挥毫泼墨 ...

  10. CSRF浅析

    概念 CSRF,Cross Site Request Forgery,跨站请求伪造. 为什么跨站的请求需要伪造? 因为浏览器实现了同源策略,这里可以将站和源视为同一个概念. 同源策略 The same ...