当前位置: 首页 > news >正文

山东省建设厅举报网站桂林公司做网站

山东省建设厅举报网站,桂林公司做网站,石家庄新闻,大连模板网站制作哪家好目录1、 资源使用概况2、互斥方法之一#xff1a;基本临界区2.1、taskENTER_CRITICAL_FROM_ISR() 和taskEXIT_CRITICAL_FROM_ISR()3、互斥方法之二#xff1a;挂起或锁定调度程序3.1 vTaskSuspendAll()3.2 xTaskResumeAll()4 互斥方法三#xff1a;互斥信号量#xff08;和… 目录1、 资源使用概况2、互斥方法之一基本临界区2.1、taskENTER_CRITICAL_FROM_ISR() 和taskEXIT_CRITICAL_FROM_ISR()3、互斥方法之二挂起或锁定调度程序3.1 vTaskSuspendAll()3.2 xTaskResumeAll()4 互斥方法三互斥信号量和二进制信号量4.1 xSemaphoreCreateMutex()4.2 实例4.3 什么叫优先级继承4.4 关于死锁4.5 递归互斥锁4.6 互斥锁和任务调度5、网守任务Gatekeeper Tasks总结1、 资源使用概况 在多任务系统中如果一个任务开始访问资源但在退出运行状态之前未完成其访问则可能会出错。如果任务使资源处于不一致状态则任何其他任务或中断对同一资源的访问都可能导致数据损坏或其他类似问题。 以上的问题是很浅显的。对外设的访问对内存的访问等都可能出现以上的问题。 互斥 为了确保始终保持数据一致性必须使用“互斥”技术管理对任务之间或任务与中断之间共享的资源的访问。目标是确保一旦任务开始访问非可重入且非线程安全的共享资源同一任务就可以独占访问该资源直到资源返回到一致状态。 FreeRTOS提供了几个可用于实现互斥的功能但最好的互斥方法是在可能的情况下因为通常不实用以不共享资源的方式设计应用程序并且每个资源只能从单个任务访问。 2、互斥方法之一基本临界区 什么是临界区基本临界区是分别被宏 taskENTER_CRITICAL() 和 taskEXIT_CRITICAL() 调用包围的代码区域。 以这种方式实现的临界区是提供互斥的一种非常粗略的方法。它们的工作方式是完全禁用中断或者禁用达到configMAX_SYSCALL_INTERRUPT_PRIORITY 设置的中断优先级这取决于所使用的 FreeRTOS 端口的中断。 由于抢先式上下文切换只能在中断中发生因此只要中断保持禁用状态调用 taskENTER_CRITICAL() 的任务就可以保证保持在运行状态直到退出临界区。 在调用 taskENTER_CRITICAL() 和调用 taskEXIT_CRITICAL() 之间不能切换到另一个任务。中断仍然可以在允许中断嵌套的 FreeRTOS 端口上执行但仅限于逻辑优先级高于分配给 configMAX_SYSCALL_INTERRUPT_PRIORITY 常量的值的中断——并且不允许这些中断调用 FreeRTOS API 函数。 临界区必须保持非常短否则将对中断响应时间产生不利影响。对taskENTER_CRITICAL的每次调用都必须与对taskEXIT_CRITICAL的调用紧密配对。 临界区嵌套是安全的因为内核会计算嵌套深度。只有当嵌套深度归零时临界区才会退出——即对之前的每一次 taskENTER_CRITICAL() 调用都执行了一次 taskEXIT_CRITICAL() 调用。 调用 taskENTER_CRITICAL() 和 taskEXIT_CRITICAL() 是任务更改运行 FreeRTOS 的处理器的中断启用状态的唯一合法方法。通过任何其他方式更改中断启用状态将使宏的嵌套计数无效 2.1、taskENTER_CRITICAL_FROM_ISR() 和taskEXIT_CRITICAL_FROM_ISR() taskENTER_CRITICAL() 和 taskEXIT_CRITICAL() 不会以“FromISR”结尾因此不得从中断服务例程中调用。 taskENTER_CRITICAL_FROM_ISR() 是 taskENTER_CRITICAL() 的中断安全版本taskEXIT_CRITICAL_FROM_ISR() 是 taskEXIT_CRITICAL() 的中断安全版本。中断安全版本仅适用于允许嵌套中断的 FreeRTOS 端口——它们在不允许中断嵌套的端口中已过时。 返回值 返回调用taskENTER_CRITIC_FROM_ISR时的中断掩码状态。必须保存返回值以便将其传递到对taskEXIT_CRITICAL_FROM_ISR()的匹配调用中。 void vAnInterruptServiceRoutine( void ) {UBaseType_t uxSavedInterruptStatus; //这个变量存储taskENTER_CRITICAL_FROM_ISR()的返回值/*ISR中的这部分可以被更高优先级的中断所中断. *//* 使用 taskENTER_CRITICAL_FROM_ISR()保护ISR的这个区域。存储taskENTER_CRITICAL_FROM_ISR()返回值并被传递给对应的 taskEXIT_CRITICAL_FROM_ISR(). */uxSavedInterruptStatus taskENTER_CRITICAL_FROM_ISR();/* 这部分内容处于taskENTER_CRITICAL_FROM_ISR()与 taskEXIT_CRITICAL_FROM_ISR()之间,所以只会被优先级高于 configMAX_SYSCALL_INTERRUPT_PRIORITY 所设定的值的中断所中断。 */taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );}基本的临界区进入非常快退出非常快而且总是确定性的因此当受保护的代码区域非常短时它们的使用非常理想。 3、互斥方法之二挂起或锁定调度程序 这是另一种建立临界区的方法。 可以通过暂停调度程序命令vTaskSuspendAll()来创建临界区域。暂停调度程序有时也称为“锁定”调度程序。 基本临界区保护代码区域不被其他任务和中断访问。通过挂起调度程序实现的临界区仅保护代码区域不被其他任务访问因为中断仍然启用。 如果关键部分太长无法通过简单地禁用中断来实现则可以通过暂停调度器来实现。然而当调度程序被挂起时中断活动会使恢复或“取消挂起”调度程序成为一个相对较长的操作因此必须考虑在每种情况下使用哪种方法最好。 3.1 vTaskSuspendAll() 调度程序通过调用vTaskSuspendAll被挂起。挂起调度程序可防止发生上下文切换但会启用中断。如果在调度程序挂起时中断请求上下文切换则该请求保持挂起状态并且仅在调度程序恢复未挂起时执行。 3.2 xTaskResumeAll() 描述 本函数恢复调度器活动跟在先前调用的vTaskSuspendAll()使调度器由挂起状态过渡为活动状态。 返回值 pdTRUE 调度器转为活动状态。这个转换引起了阻塞中的上下文进行切换。 pdFALSE 调度程序转换到 Active 状态并且转换没有导致发生上下文切换或者调度程序由于对vTaskSuspendAll() 的嵌套调用而留在 Suspended 状态。 注意 调用vTaskSuspendAll()使调度器被挂起。当调度器被挂起中断仍然可用但上下文切换不再进行。如果调度器挂起时有一个上下文切换请求那么这个请求将被保持挂起直到调度器被恢复。 对 vTaskSuspendAll() 和 xTaskResumeAll() 的调用嵌套是安全的因为内核会计算嵌套深度。只有当嵌套深度归零时调度程序才会恢复——也就是说对于之前对 vTaskSuspendAll() 的每一次调用都会执行一次xTaskResumeAll() 调用。 4 互斥方法三互斥信号量和二进制信号量 Mutex 是一种特殊类型的二进制信号量用于控制对两个或多个任务之间共享的资源的访问。 MUTEX 一词源于“MUTual EXclusion”。 configUSE_MUTEXES 必须在 FreeRTOSConfig.h 中设置为 1 才能使用互斥锁。 在互斥场景中使用互斥锁时可以将互斥锁视为与共享资源相关联的令牌。对于合法访问资源的任务它必须首先成功“获取”令牌成为令牌持有者。当令牌持有者用完资源后它必须“归还”令牌。只有当令牌被返回后另一个任务才能成功获取令牌然后安全地访问相同的共享资源。除非拥有令牌否则不允许任务访问共享资源。 4.1 xSemaphoreCreateMutex() 描述 该函数创建一个互斥类型的信号量并返回一个可以引用互斥的句柄。 每个互斥类型的信号量都需要少量的RAM来保存信号量的状态。如果使用SemaphoreCreateMutex创建互斥体则所需的RAM将自动从FreeRTOS堆中分配。如果使用xSemaphoreCreateMutexStatic创建互斥锁则RAM由应用程序编写器提供这需要额外的参数但允许在编译时静态分配RAM。 返回值 NULL 如果无法创建信号量则返回NULL因为没有足够的堆内存供FreeRTOS分配信号量数据结构。 除NULL外的其它值已成功创建信号量。返回的值是一个句柄通过它可以引用创建的信号量。 注意事项 二进制信号量和互斥量非常相似但确实有一些细微的区别。 互斥包含优先级继承机制而二进制信号量不包含。这使得二进制信号量成为实现同步任务之间或任务与中断之间的更好选择而互斥体则是实现简单互斥的更好选择。 二进制信号量–用于同步的二进制信号量在成功“获取”获得后无需“返回”。通过让一个任务或中断“给出”信号量另一个任务“接受”信号量来实现任务同步。 互斥锁–如果另一个优先级较高的任务试图获得相同的互斥锁则持有互斥锁的任务的优先级将提高。已经持有互斥锁的任务被称为“继承”试图“获取”同一互斥锁任务的优先级。当互斥体被返回时继承的优先级将被“取消继承”在持有互斥体时继承了更高优先级的任务在返回互斥体时将返回其原始优先级。 获得互斥对象的任务必须始终返回 xSemaphoreTake()互斥对象否则任何其他任务都无法获得相同的互斥对象。 互斥和二进制信号量都是使用SemaphoreHandle_t类型的变量引用的并且可以在任何使用该类型参数的API函数中使用。 configSUPPORT_DYNAMIC_ALLOCATION必须在FreeRTOSConfig.h中设置为1或者干脆不定义才能使用此函数。 4.2 实例 此示例创建了一个名为prvNewPrintString的vPrintString新版本然后从多个任务调用新函数。prvNewPrintString在功能上与vPrintString相同但使用互斥锁而不是锁定调度器来控制对标准输出的访问。prvNewPrintString的实现如下所示。 static void prvNewPrintString( const char *pcString ) {/* 互斥信号量xMutex已在调度器启动前被创建具体见其它代码. 这里用了portMAX_DELAY,使得如果得不到xMutex时就一直阻塞。因此这里要么阻塞要么获得信号量所以这里可以不用在其后去判断获得信号量 是否成功。如果后面的阻塞等待时间参数不是portMAX_DELAY那么就需要在其后判断获取信号量是否成功 */xSemaphoreTake( xMutex, portMAX_DELAY );{/* 只有成功获取互斥对象后才会执行以下行。标准输出现在可以自由访问因为任何时候只有一个任务可以拥有互斥锁。 */printf( %s, pcString );fflush( stdout );}xSemaphoreGive( xMutex );/* 互斥锁必须被主动给回。 */ }prvNewPrintString由prvPrintTask实现的任务的两个实例重复调用。每个调用之间使用随机延迟时间。task参数用于向任务的每个实例传递唯一字符串。prvPrintTask的实现如下所示。 static void prvPrintTask( void *pvParameters ) {char *pcStringToPrint;const TickType_t xMaxBlockTimeTicks 0x20;pcStringToPrint ( char * ) pvParameters;for( ;; ){prvNewPrintString( pcStringToPrint );vTaskDelay( ( rand() % xMaxBlockTimeTicks ) );}}prvPrintTask() 的两个实例以不同的优先级创建因此较低优先级的任务有时会被较高优先级的任务抢占。由于使用互斥锁来确保每个任务都可以互斥地访问终端因此即使发生抢占显示的字符串也将是正确的不会被破坏。可以通过减少任务在阻塞状态下花费的最长时间来增加抢占的频率该时间由 xMaxBlockTimeTicks 常量设置。 int main( void ) {xMutex xSemaphoreCreateMutex(); //创建互斥锁用于对标准输出资源的管理/* Check the semaphore was created successfully before creating the tasks. */if( xMutex ! NULL ){/* 创建两个写入stdout任务的实例。两个实例被传入不同的字符串做为参数用于任务的打印输出。两个任务优先级不同使他们能互相抢占。*/xTaskCreate( prvPrintTask, Print1, 1000, Task 1 ***************************************\r\n, 1, NULL );xTaskCreate( prvPrintTask, Print2, 1000, Task 2 ---------------------------------------\r\n, 2, NULL );vTaskStartScheduler();}for( ;; ); }以上代码的输出结果哪下 4.3 什么叫优先级继承 要明白互斥信号量所具有的优先级继承概念首先要知道什么叫优先级反转。 优先级反转 当较低优先级的任务拥有了互斥锁后则按常理较高优先级的任务必须等待这个较低优先级的任务放弃对互斥体的控制。因此更高优先级的任务是以这种方式被较低优先级任务延迟称为“优先级反转”。如果中等优先级任务开始执行而高优先级任务正在等待信号量结果将是高优先级任务等待低优先级任务则这种不良行为将被进一步夸大而低优先级任务甚至无法执行。执行。这种最坏的情况如下图 所示。 1、三个不同的优先级任务。LP,MP,HP分别指低优先级中等优先级最高优先级。LP在执行时在被HP抢占前就获得了互斥锁。 2、HP抢占了LP后想要去获得互斥锁但因为此时是LP占用互斥锁因此HP只能进入阻塞状态去等待LP释放互斥锁。 3、LP继续执行但是在释放互斥锁之前又被 MP抢占了时间片。 4、这时HP继续等待LP释放互斥锁但是LP现在却不能执行更不能释放互斥锁因为此时是MP在不断抢占执行。 以上的这个“优先级反转”会破坏任务间的正常调度造成系统的不确定性。因此这里必须引入“优先级继承”的技术来解决这个问题。 优先级继承 FreeRTOS 互斥体和二进制信号量非常相似——不同之处在于互斥体包含基本的“优先级继承”机制而二进制信号量则没有。优先级继承是一种将优先级反转的负面影响降至最低的方案。它不会“修复”优先级反转而只是通过确保返转总是有时间限制来减轻其影响。但是优先级继承使系统时序分析变得复杂依赖它来进行正确的系统操作并不是一个好的做法。 优先级继承的工作原理是将互斥锁持有者的优先级临时提高到试图获得相同互斥锁的最高优先级任务的优先级。持有互斥锁的低优先级任务“继承”等待互斥锁的任务的优先级。下图 演示了这一点。互斥锁持有者的优先级在将互斥锁归还时自动重置为其原始值。 1、三个不同的优先级任务。LP,MP,HP分别指低优先级中等优先级最高优先级。LP在执行时在被HP抢占前就获得了互斥锁。 2、HP抢占了LP后想要去获得互斥锁但因为此时是LP占用互斥锁因此HP只能进入阻塞状态去等待LP释放互斥锁。 3、 LP 任务阻止 HP 任务执行因此继承了HP 任务的优先级。 LP 任务现在不能被 MP 任务抢占因此优先级反转存在的时间量被最小化。当 LP 任务将互斥锁返回时它会返回其原始优先级。 4、返回互斥锁的 LP 任务导致 HP 任务作为互斥锁持有者退出阻塞状态。当 HP 任务使用互斥体完成时它会将其返回。 MP 任务仅在 HP 任务返回 Blocked 状态时执行因此 MP 任务永远不会阻塞 HP 任务。 4.4 关于死锁 死锁或致命拥抱 “死锁”是使用互斥锁进行互斥的另一个潜在陷阱。死锁有时也以更具戏剧性的名称“致命拥抱”而闻名。 当两个任务因为都在等待对方持有的资源而无法继续时就会发生死锁。考虑以下场景其中任务 A 和任务 B 都需要获取互斥锁 X 和互斥锁 Y 才能执行操作 1、 任务 A 执行并成功获取 mutex X。 2、 任务 A 被任务 B 抢占。 3、 任务 B 在尝试同时获取互斥体 X 之前成功地获取了互斥体 Y——但互斥体 X 由任务 A 持有因此对任务 B 不可用。任务 B 选择 进入阻塞状态以等待互斥体 X 被释放。 4、 任务A继续执行。它尝试获取互斥体 Y——但互斥体 Y 由任务 B 持有因此对任务 A 不可用。任务 A 选择进入阻塞状态以等待 互斥体 Y 被释放。 在这个场景结束时任务 A 正在等待任务 B 持有的互斥锁而任务 B 正在等待任务 A 持有的互斥锁。由于两个任务都无法继续 因此发生了死锁。 与优先级倒置一样避免死锁的最佳方法是在设计时考虑其潜力并设计系统以确保不会发生死锁。特别是正如前面所述任务无限期地等待没有超时以获得互斥锁通常是不好的做法。 4.5 递归互斥锁 递归互斥锁 任务也可能与自身发生死锁。如果一个任务尝试多次使用同一个互斥锁而不首先返回互斥锁就会发生这种情况。考虑以下场 景 1、任务成功获取互斥量。 2、 在持有互斥锁的同时任务调用库函数。 3、库函数的实现尝试取同一个互斥体进入阻塞状态等待互斥体可用。 在这个场景结束时任务处于阻塞状态等待互斥锁返回但任务已经是互斥锁持有者。发生死锁是因为任务处于阻塞状态等待自 己。 可以通过使用递归互斥锁代替标准互斥锁来避免这种类型的死锁。递归互斥锁可以被同一个任务多次“获取”并且只有在对“ 获取”递归互斥锁的每个先前调用都执行了一次“给予”递归互斥锁的调用后才会返回。标准互斥锁和递归互斥锁的创建和使用方式类似 -使用 xSemaphoreCreateMutex() 创建标准互斥锁。递归互斥锁是使用 xSemaphoreCreateRecursiveMutex() 创建的。这两个API 函数有相同的原型。 -使用 xSemaphoreTake()“获取”标准互斥锁。递归互斥锁是使用 xSemaphoreTakeRecursive() 来“获取”的。这两个 API 函数具有相同的原型。 -使用 xSemaphoreGive() “给定”标准互斥锁。递归互斥锁是使用 xSemaphoreGiveRecursive() “给定”的。这两个 API 函数具有相同的原型。 演示了如何创建和使用递归互斥锁。 SemaphoreHandle_t xRecursiveMutex; //递归互斥锁的变量/* 以下是创建和使用一个递归互斥锁的任务 */ void vTaskFunction( void *pvParameters ) {const TickType_t xMaxBlock20ms pdMS_TO_TICKS( 20 );xRecursiveMutex xSemaphoreCreateRecursiveMutex(); //创建递归互斥锁configASSERT( xRecursiveMutex ); //检查互斥锁是否被创建成功for( ;; ){/* ... */if( xSemaphoreTakeRecursive( xRecursiveMutex, xMaxBlock20ms ) pdPASS ) //获得递归互斥锁{/*已成功获取递归互斥对象。该任务现在可以访问互斥锁正在保护的资源。此时递归调用计数这是对xSemaphoreTakeRecursive的嵌套调用数为1因为递归互斥体只执行了一次。当它已经持有递归互斥锁时任务将再次获取互斥锁。在实际应用程序中这只可能发生在该任务调用的子函数中因为没有实际的理由故意多次使用相同的互斥锁。调用任务已经是互斥体持有者因此对xSemaphoreTakeRecursive的第二次调用只会将递归调用计数增加到2。*/xSemaphoreTakeRecursive( xRecursiveMutex, xMaxBlock20ms );/* ... *//* 任务在完成对互斥锁所保护的资源的访问后返回互斥锁。此时递归调用计数为2因此对xSemaphoreGiveRecursive的第一次调用不会返回互斥体。相反它只是将递归调用计数减回到1。*/xSemaphoreGiveRecursive( xRecursiveMutex );/* 下一次对xSemaphoreGiveRecursive的调用将递归调用计数减为0因此这次返回递归互斥。*/xSemaphoreGiveRecursive( xRecursiveMutex );/*现在对 xSemaphoreTakeRecursive()的每一次调用都执行了一次xSemaphoreGiveRecursive因此该任务不再是互斥体持有者。 }} }4.6 互斥锁和任务调度 如果两个不同优先级的任务使用同一个互斥锁那么 FreeRTOS 调度策略会明确任务执行的顺序能够运行的优先级最高的任务将被选为进入运行状态的任务。例如如果一个高优先级任务处于阻塞状态以等待一个低优先级任务持有的互斥锁那么一旦低优先级任务返回互斥锁高优先级任务就会抢占低优先级任务 . 然后高优先级任务将成为互斥锁持有者。 然而当任务具有相同优先级时通常会错误地假设任务将执行的顺序。如果 Task 1 和 Task 2 具有相同的优先级并且 Task 1 处 于 Blocked 状态以等待 Task 2 持有的互斥锁那么当 Task 2“给予”互斥锁时Task 1 不会抢占 Task 2。相反任务 2 将保持在运 行状态而任务 1 将简单地从阻塞状态移动到就绪状态。这种情况如下图所示其中垂直线标记发生滴答中断的时间。 1、Task2运行一个时间片在此期间拿到一个互斥锁。 2、在下一个时间片Task1开始执行。 3、Task1试图拿到互斥锁此时互斥锁被Task2拥有因此task1进入阻塞等待。 4、在t2-t3剩下的时间片里Task2运行并且持续到下一个时间片t3-t4 5、在t3-t4时间片里Task2交出互斥锁这里task1立即从阻塞状态切换到ready状态。 6、task1要到t3-t4时间片结束后才能进入running状态。 在上图 所示的场景中一旦互斥锁可用FreeRTOS 调度程序就不会立即使Task1 成为运行状态任务因为 1、 任务 1 和任务 2 具有相同的优先级因此除非任务 2 进入 Blocked 状态否则在下一次滴答中断之前不应切换到任务 1假设 FreeRTOSConfig.h 中的 configUSE_TIME_SLICING 设置为 1。 2、 如果任务在紧密循环中使用互斥锁并且每次任务“给予”互斥锁时都会发生上下文切换那么任务只会在短时间内保持运行状 态。如果两个或多个任务在紧密循环中使用相同的互斥锁则在任务之间快速切换会浪费处理时间。 如果多个任务在紧密循环中使用互斥锁并且使用互斥锁的任务具有相同的优先级则必须注意确保任务获得大致相等的处理时间。 下图说明了任务可能无法获得相等处理时间的原因下图显示了如果以相同优先级创建如下代码所示任务的两个实例可能会发生的执行顺序。 /* 在紧密循环中使用互斥锁的任务的实现。该任务在本地缓冲区中创建一个文本字符串然后将该字符串写入显示器。对显示器的访问受到互斥锁的保护*/ void vATask( void *pvParameter ) {extern SemaphoreHandle_t xMutex;char cTextBuffer[ 128 ];for( ;; ){/* 生成字符串--这是一个快速的操作 */vGenerateTextInALocalBuffer( cTextBuffer );/* 获得互斥锁用于保护显示器的访问. */xSemaphoreTake( xMutex, portMAX_DELAY );/* 将生成的字符串写到显示器---这是一个慢速的操作. */vCopyTextToFrameBuffer( cTextBuffer );/* 字符串写到显示器后返回互斥锁 */xSemaphoreGive( xMutex );} }在这个任务中创建字符串是一项快速操作而更新显示是一项缓慢的操作。因此由于在更新显示时保持互斥锁任务将在其大部分运行时间保持互斥锁。 1、Task2 在t1-t2时间片里处于运行状态同时在此期间获得了互斥锁。 2、Task1在下一个时间片开始运行。 3、Task1运行期间想要获取互斥锁但task2并没有释放互斥锁所以task1进入阻塞状态 4、task2在t2-t3的剩余时间里开始运行并保持到下一个时间片t3-t4。 5、task2在此处返回互斥锁。立即使task1退出阻塞状态进入ready状态。但由于进间片还未完成所以task2继续运行。 6、在task2继续运行的进间片里task2又获得互斥锁。 7、在t4时间片开始时task1进入运行状态但立即又因为拿不到互斥锁而进入阻塞状态。 上图中的第 7 步显示任务 1 重新进入阻塞状态—这发生在 xSemaphoreTake() API 函数内部。 上图 展示了任务 1 将被阻止获得互斥锁直到时间片的开始与任务 2 不是互斥锁持有者的短周期之一重合。 通过在调用 xSemaphoreGive() 之后添加对 taskYIELD() 的调用可以避免上图 中所示的情况。这在下面的代码中进行了演示如 果在任务持有互斥锁时滴答计数发生变化则调用 taskYIELD()。 void vFunction( void *pvParameter ) {extern SemaphoreHandle_t xMutex;char cTextBuffer[ 128 ];TickType_t xTimeAtWhichMutexWasTaken;for( ;; ){/* 生成字符串--这是一个快速的操作时间很短。 */vGenerateTextInALocalBuffer( cTextBuffer );/* 获得互斥锁用于保护对显示器的访问。 */xSemaphoreTake( xMutex, portMAX_DELAY );/* 记录获得互斥锁的时间。 */xTimeAtWhichMutexWasTaken xTaskGetTickCount();/* 将字符串写入到显示器--这是一个慢速的操作时间较长 */vCopyTextToFrameBuffer( cTextBuffer );/* 完成写入显示器后返回互斥锁. */xSemaphoreGive( xMutex );/* 如果在每次迭代中调用taskYIELD则此任务只会在短时间内保持运行状态并且快速切换任务会浪费处理时间。因此只有在互斥锁被持有时滴答计数发生变化时才调用taskYIELD。 */if( xTaskGetTickCount() ! xTimeAtWhichMutexWasTaken ){taskYIELD();}} }5、网守任务Gatekeeper Tasks 网守任务提供了一种实现互斥的干净方法没有优先级反转或死锁的风险。 网守任务是对资源拥有唯一所有权的任务。只有网守任务被允许直接访问资源——任何其他需要访问资源的任务只能通过使用网守的服务间接访问。 接下来的示例 重写 vPrintString() 以使用网守任务。示例为 vPrintString() 提供了另一种替代实现。这一次gatekeeper 任务用于管理对标准输出的访问。当一个任务想要将消息写入标准输出时它不会直接调用打印函数而是将消息发送给gatekeeper。 网守任务使用 FreeRTOS 队列来序列化对标准输出的访问。任务的内部实现不必考虑互斥因为它是唯一允许直接访问标准的任务。 网守任务大部分时间都处于阻塞状态等待消息到达队列。当消息到达时网守只需将消息写入标准输出然后返回阻塞状态以等待下一条消息。 中断可以发送到队列因此中断服务例程也可以安全地使用网守的服务将消息写入终端。在这个例子中一个ticks钩子函数用于每 200 个ticks写出一条消息。 tick钩子函数在tick中断的上下文中执行因此必须保持非常短必须仅使用适量的堆栈空间并且不得调用任何不以“FromISR()”结尾的 FreeRTOS API 函数。 调度程序总是在tick钩子函数之后立即执行因此从tick钩子调用的中断安全 FreeRTOS API 函数不需要使用它们的pxHigherPriorityTaskWoken 参数该参数可以设置为 NULL。 static void prvStdioGatekeeperTask( void *pvParameters ) {char *pcMessageToPrint;/* 这是唯一允许写入标准输出的任务。任何其他想要将字符串写入输出的任务都不会直接访问标准输出而是将字符串发送到此任务。由于只有此任务访问标准输出因此在任务本身的实现中不需要考虑互斥或串行化问题。 */for( ;; ){/* 接收队列内的信息 */xQueueReceive( xPrintQueue, pcMessageToPrint, portMAX_DELAY );printf( %s, pcMessageToPrint );fflush( stdout );} }写入队列的任务下 所示。如前所述创建了两个单独的任务实例并使用 task 参数将任务写入队列的字符串传递给任务。 static void prvPrintTask( void *pvParameters ) {int iIndexToString;const TickType_t xMaxBlockTimeTicks 0x20;/* 该任务的两个实例。任务的参数用于传送字符串数组的索引。*/iIndexToString ( int ) pvParameters;for( ;; ){/* 不直接打印字符串而是通过队列将指向字符串的指针传递给看门人任务。队列是在调度程序启动之前创建的因此在该任务第一次执行时已经存在。未指定块时间因为队列中应始终有空间。*/xQueueSendToBack( xPrintQueue, ( pcStringsToPrint[ iIndexToString ] ), 0 );vTaskDelay( ( rand() % xMaxBlockTimeTicks ) );}}tick钩子函数计算它被调用的次数每次计数达到 200 时将其消息发送给网守任务。仅出于演示目的tick钩子写入队列的前面任务写入后面的队列。tick钩子实现如下所示。 tick钩子或滴答回调是内核在每次滴答中断期间调用的函数。要使用tick挂钩功能 在 FreeRTOSConfig.h 中将 configUSE_TICK_HOOK 设置为 1。使用的钩子函数的原型如 void vApplicationTickHook( void ) {static int iCount 0;/* 每200个tick打印一个信息。信息并不是直接输出而是发送给了gatekeeper任务。 */iCount;if( iCount 200 ){xQueueSendToFrontFromISR( xPrintQueue, ( pcStringsToPrint[ 2 ] ), NULL );iCount 0;} }像往常一样 main() 创建运行示例所需的队列和任务然后启动调度程序。 main() 的实现如清单 131 所示。 /* 用于打印的字符串数组用于传送给网守任务 gatekeeper. */ static char *pcStringsToPrint[] {Task 1 ****************************************************\r\n,Task 2 ----------------------------------------------------\r\n,Message printed from the tick hook interrupt ##############\r\n }; /*-----------------------------------------------------------*/ /* 声明QueueHandle_t类型的变量。该队列用于从打印任务发送消息并将tick中断发送到看门人任务。*/ QueueHandle_t xPrintQueue; /*-----------------------------------------------------------*/ int main( void ) {xPrintQueue xQueueCreate( 5, sizeof( char * ) );/* Check the queue was created successfully. */if( xPrintQueue ! NULL ){/* 创建两个向网关守卫发送消息的任务实例。任务使用的字符串的索引通过任务参数xTaskCreate的第四个参数传递给任务。任务以不同的优先级创建因此较高优先级的任务偶尔会抢占较低优先级的任务。*/xTaskCreate( prvPrintTask, Print1, 1000, ( void * ) 0, 1, NULL );xTaskCreate( prvPrintTask, Print2, 1000, ( void * ) 1, 2, NULL );/* 创建看门人任务。这是唯一允许直接访问标准输出的任务。 */xTaskCreate( prvStdioGatekeeperTask, Gatekeeper, 1000, NULL, 0, NULL );vTaskStartScheduler();}for( ;; ); }执行示例时产生的输出如图 70 所示。可以看出源自任务的字符串和源自中断的字符串都正确打印出来没有损 坏。 网守任务的优先级低于打印任务因此发送到网守的消息将保留在队列中直到两个打印任务都处于阻塞状态。在 某些情况下为网守分配更高的优先级是合适的因此消息会立即得到处理——但这样做的代价是网守延迟较低优 先级的任务直到它完成对受保护资源的访问。 总结 多任务系统中任务对资源占用以及协调是非常重要的。即要保证占用资源的任务能不被干扰的使用也要防止出现死锁。FreeRTOS针对不同类型的使用场景提供了从禁用中断禁用调度这类系统级的资源使用手段。也提供了互斥信号量嵌套互斥信号量等应用级的资源使用手段。也提供了Gatekeeper Tasks网守任务方式的资源使用方案。 因为任务调度模式导至的多种使用资源可能出现死锁的场景FreeRTOS提供了优先级继承递归互斥锁的机制解决。分析了对于多个任务在紧密循环中使用互斥锁并且使用互斥锁的任务具有相同的优先级时可能出现的问题以及解决的方法。
http://www.hkea.cn/news/14432914/

相关文章:

  • 网页设计与网站建设在线作业网站开发 外包公司
  • 广东专业高端网站建设杭州优化seo公司
  • 做代理记账网站网站开发推荐英文字体
  • 二 加强门户网站建设网站被k了怎么办
  • 手机膜+东莞网站建设做一个网站成本多少钱
  • 做照片的网站山西网站建设开发
  • 合肥蜀山网站开发2024年新闻热点事件摘抄
  • 酒类网站建设网络营销中的seo与sem
  • 两学一做网上答题网站wordpress小程序直播
  • 公司免费招聘网站网站分析百度
  • 个人网站什么语言做wordpress插件没有设置
  • 怎么塔建网站网站建设吉金手指排名13
  • 网站php怎么做的wordpress发布文章关键词
  • 哪里有做空包网站的商务网站建设实验书
  • 建设视频网站的视频源学院二级网站建设方案模板
  • 汽车网站案例网页设计wordpress搭建学校网站
  • 荣茂网站建设免费网页在线代理服务器
  • 祁阳网站建设贤邦网站建设app开发
  • 依宝诺手表官方网站内江seo
  • wordpress+主题加速苏州seo公司排名
  • 建设网站市场分析泰州网站建设设计
  • 一级a做爰精免费网站徐州做网站哪家好
  • 什么是网站组件公司官方网站建设申请
  • 河南创达建设工程管理有限公司网站网站开发一个多少钱
  • “网站制作”电子商务营销与传统营销的区别
  • 网站建设顾问站建海阳市住房和城乡建设局官方网站
  • 梁平网站wordpress看不到表格
  • 浙江建设信息港网站上海天华建筑设计有限公司合肥分公司
  • 温州阀门外贸网站建设怎么拿到百度推广的代理
  • wordpress头部标签描述网站关键词优化排名