网站运营 策划 推广 维护,做网站需要了解的内容,成都微信小程序开发平台,17一起做网站appFreeRTOS信号量 一、概述二、PV原语三、函数接口1.创建一个计数信号量2.删除一个信号量3.信号量释放4.在中断释放信号量5.获取一个信号量#xff0c;可以是二值信号量、计数信号量、互斥量。6.在中断获取一个信号量#xff0c;可以是二值信号量、计数信号量7.创建一个二值信号… FreeRTOS信号量 一、概述二、PV原语三、函数接口1.创建一个计数信号量2.删除一个信号量3.信号量释放4.在中断释放信号量5.获取一个信号量可以是二值信号量、计数信号量、互斥量。6.在中断获取一个信号量可以是二值信号量、计数信号量7.创建一个二值信号量 四、示例代码1、计数型信号量2、二值信号量-保护共享资源 五、优先级翻转1、概述2、解决方案  一、概述 \quad 信号量Semaphore。  \quad 信号量常用于任务的同步通过该信号就能够控制某个任务的执行这个信号具有计数值因此可以称为计数信号量。  \quad 计数信号量可以用于资源管理允许多个任务获取信号量访问共享资源但会限制任务的最大数目初值常为共享资源的数量。访问的任务数达到可支持的最大数目时会阻塞其他试图获取该信号量的任务直到有任务释放了信号量。这就是计数型信号量的运作机制虽然计数信号量允许多个任务访问同一个资源但是也有限定比如某个资源限定只能有3个任务访问那么第4个任务访问的时候会因为获取不到信号量而进入阻塞等到有任务比如任务1释放掉该资源的时候第4个任务才能获取到信号量从而进行资源的访问其运作的机制具体见下图。 二、PV原语 
$\quad$1965年荷兰学者Dijkstra提出了利用信号量机制解决进程同步问题信号量正式成为有效的进程同步工具现在信号量机制被广泛的用于单处理机和多处理机系统以及计算机网络中。  \quad 信号量S是一个整数S大于等于零时代表可供并发进程使用的资源实体数但S小于零时则表示正在等待使用临界区的进程数。  \quad Dijkstra同时提出了对信号量操作的PV原语。 
P原语操作的动作是 1S减1 2若S减1后仍大于或等于零则进程继续执行 3若S减1后小于零则该进程被阻塞后进入与该信号相对应的队列中然后转进程调度。 
V原语操作的动作是 1S加1 2若相加结果大于零则进程继续执行 3若相加结果小于或等于零则从该信号的等待队列中唤醒一等待进程然后再返回原进程继续执行或转进程调度。 PV操作对于每一个进程来说都只能进行一次而且必须成对使用。在PV原语执行期间不允许有中断的发生。 \quad 信号量的P、V操作P表示申请一个资源每次P操作使信号量减1V是释放一个资源每次V操作使信号量加1。信号量表示的是当前可用的资源个数当信号量为负时申请资源的进程任务就只能等待了。所以信号量是负的多少就表明有多少个进程任务申请了资源但无资源可用只能处于等待状态。  \quad 除了访问共享资源外亦可中断/任务控制某任务的执行称之为“单向同步”。 三、函数接口 
信号量使能文件路径freertos.h另外用户代码要包含semphr.h。 
#ifndef configUSE_COUNTING_SEMAPHORES#define configUSE_COUNTING_SEMAPHORES 1
#endif1.创建一个计数信号量 
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);参数说明 
uxMaxCount-计数信号量的最大值当达到这个值的时候信号量不能再被释放。uxInitialCount-创建计数信号量的初始值。 
返回值 
如果创建成功则返回一个计数信号量句柄用于访问创建的计数信号量。如果创建不成功则返回 NULL。 
2.删除一个信号量 \quad vSemaphoreDelete()用于删除一个信号量包括二值信号量计数信号量互斥量和递 归互斥量。如果有任务阻塞在该信号量上那么不要删除该信号量。 
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );参数说明 
xSemaphore-信号量句柄。 
返回值  \quad 无。 
3.信号量释放 \quad xSemaphoreGive()是一个用于释放信号量的宏真正的实现过程是调用消息队列通用发送函数。释放的信号量对象必须是已经被创建的可以用于二值信号量、计数信号量、互斥量的释放但不能释放由函数xSemaphoreCreateRecursiveMutex()创建的递归互斥量。此外该函数不能在中断中使用。 
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );参数说明 
xSemaphore-信号量句柄。 
返回值 
pdTRUE-信号量被释放。pdFALSE-信号量发生错误 
4.在中断释放信号量 \quad 用于释放一个信号量带中断保护。被释放的信号量可以是二进制信号量和计数信号量。 
BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,signed BaseType_t *pxHigherPriorityTaskWoken)参数说明 
xSemaphore-信号量句柄。pxHigherPriorityTaskWoken-一个或者多个任务有可能阻塞在同一个信号量上调用函数xSemaphoreGiveFromISR()会唤醒阻塞在该信号量上优先级最高的信号量入队任务如果被唤醒的任务的优先级大于或者等于被中断的任务的优先级那么形参 pxHigherPriorityTaskWoken 就会被设置为 pdTRUE然后在中断退出前执行一次上下文切换中断退出后则直接返回刚刚被唤醒的高优先级的任务。从 FreeRTOS V7.3.0 版本开始pxHigherPriorityTaskWoken 是一个可选的参数可以设置为 NULL。 
返回值 
pdTRUE-信号量被释放pdFALSE-信号量 发生错误 
5.获取一个信号量可以是二值信号量、计数信号量、互斥量。 \quad xSemaphoreTake()函数用于获取信号量不带中断保护。获取的信号量对象可以是二 值信号量、计数信号量和互斥量但是递归互斥量并不能使用这个 API 函数获取。其实获取信号量是一个宏真正调用的函数是 xQueueGenericReceive ()。该宏不能在中断使用 而是必须由具体中断保护功能的 xQueueReceiveFromISR()版本代替。 
BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore,TickType_t xTicksToWait );参数说明 
xSemaphore-信号量句柄。xTicksToWait -等待信号量可用的最大超时时间单位为 tick即系统节拍周期。 如果宏 INCLUDE_vTaskSuspend 定义为 1 且形参 xTicksToWait 设置为 portMAX_DELAY 则任务将一直阻塞在该信号量上即没有超时时间。 
返回值 
获取成功则返回pdTRUE在指定的超时时间中没有获取成功则返回errQUEUE_EMPTY。 \quad 从该宏定义可以看出释放信号量实际上是一次消息出队操作阻塞时间由用户指定xTicksToWait当有任务试图获取信号量的时候当且仅当信号量有效的时候任务才能读获取到信号量。如果信号量无效在用户指定的阻塞超时时间中该任务将保持阻塞状态以等待信号量有效。当其它任务或中断释放了有效的信号量该任务将自动由阻塞态转移为就绪态。当任务等待的时间超过了指定的阻塞时间即使信号量中还是没有可用信号量任务也会自动从阻塞态转移为就绪态。 6.在中断获取一个信号量可以是二值信号量、计数信号量 \quad xSemaphoreTakeFromISR()是函数 xSemaphoreTake()的中断版本用于获取信号量是一个不带阻塞机制获取信号量的函数获取对象必须由是已经创建的信号量信号量类型可以是二值信号量和计数信号量它与 xSemaphoreTake()函数不同它不能用于获取互斥xSemaphoreTake()函数不同它不能用于获取互斥量因为互斥量不可以在中断中使用并且互斥量特有的优先级继承机制只能在任务中起作用而在中断中毫无意义。 
BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore, signed BaseType_t *pxHigherPriorityTaskWoken)功能描述在中断中获一个信号量其实很少在中断中获取信号量。可以是二值信号量、计数信号量。 
参数说明 
xSemaphore-信号量句柄。pxHigherPriorityTaskWoken-一个或者多个任务有可能阻塞在同一个信号量上调用函数xSemaphoreTakeFromISR()会唤醒阻塞在该信号量上优先级最高的信号量入队任务如果被唤醒的任务的优先级大于或者等于被中断的任务的优先级那么形参 pxHigherPriorityTaskWoken 就会被设置为 pdTRUE然后在中断退出前执行一次上下文切换中断退出后则直接返回刚刚被唤醒的高优先级的任务可提高实时性。从 FreeRTOS V7.3.0 版本开始pxHigherPriorityTaskWoken 是一个可选的参数可以设置为 NULL。 
返回值: 
获取成功则返回 pdTRUE没有获取成功则返回 errQUEUE_EMPTY没有获取成功是因为信号量不可用。 
7.创建一个二值信号量 \quad 二值信号量通常用于互斥访问或同步二值信号量和互斥信号量非常类似但是还是有一些细微的差别互斥信号量拥有优先级继承机制二值信号量没有优先级继承。因此二值信号另更适合用于同步(任务与任务或任务与中断的同步即控制任务的执行)而互斥信号量适合用于简单的互斥访问。 
SemaphoreHandle_t xSemaphoreCreateBinary( void );参数说明  \quad 无 
返回值 
如果创建成功则返回一个二值信号量句柄用于访问创建的二值信号量。如果创建不成功则返回 NULL。 
四、示例代码 
1、计数型信号量 
#include semphr.hSemaphoreHandle_t  g_sem_count;int main(void)
{/* 创建计数型的信号量 ,计数值递增到最大值就不能再增加了初值为0*/g_sem_countxSemaphoreCreateCounting(255,0);   }void app_task1(void *pvParameters)
{while(1){	/* 发送信号 */xSemaphoreGive(g_sem_count);xSemaphoreGive(g_sem_count);xSemaphoreGive(g_sem_count);printf([app_task1] running ...........\r\n);vTaskDelay(1000);}
}void app_task2(void *pvParameters)
{while(1){	/* 等待信号量,portMAX_DELAY一直阻塞等待 */xSemaphoreTake(g_sem_count,portMAX_DELAY);printf([app_task2] running ...........\r\n);}
}现象  
2、二值信号量-保护共享资源 
前提使用二值信号量的任务优先级相同若不相同可能会导致优先级翻转。 
#include semphr.hSemaphoreHandle_t  g_sem_binary;int main(void)
{/* 创建二值型信号量*/vSemaphoreCreateBinary(g_sem_binary);
}void app_task1(void *pvParameters)
{while(1){	/* 等待信号量,portMAX_DELAY一直阻塞等待 */xSemaphoreTake(g_sem_binary,portMAX_DELAY);printf([app_task1] running ...........\r\n);/* 释放信号 */xSemaphoreGive(g_sem_binary);vTaskDelay(1000);}
}void app_task2(void *pvParameters)
{while(1){	/* 等待信号量,portMAX_DELAY一直阻塞等待 */xSemaphoreTake(g_sem_binary,portMAX_DELAY);printf([app_task2] running ...........\r\n);/* 释放信号 */xSemaphoreGive(g_sem_binary);vTaskDelay(1000);}
}现象  
五、优先级翻转 
1、概述 \quad 优先级翻转是当一个高优先级任务通过信号量机制访问共享资源时该信号量已被一低优先级任务占有因此造成高优先级任务被许多具有较低优先级任务阻塞实时性难以得到保证。 \quad 高优先级任务无法运行而低优先级任务任务M、任务L可以运行的现象称为“优先级翻转”。  1任务H和任务M处于挂起状态等待某一事件的发生任务L正在运行。 2某一时刻任务L想要访问共享资源在此之前它必须先获得对应该资源的信号量。 3任务L获得信号量并开始使用该共享资源。 4由于任务H优先级高它等待的事件发生后便剥夺了任务L的CPU使用权。 5任务H开始运行。 6任务H运行过程中也要使用任务L正在使用着的资源由于该资源的信号量还被任务L占用着任务H只能进入阻塞等待状态等待任务L释放该信号量。 7任务L继续运行。 8由于任务M的优先级高于任务L当任务M等待的事件发生后任务M剥夺了任务L的CPU使用权。 9任务M处理该处理的事。 10任务M执行完毕后将CPU使用权归还给任务L。 11最终任务L完成所有的工作并释放了信号量到此为止由于实时内核知道有个高优先级的任务在等待这个信号量故内核做任务切换。 \quad 在这种情况下任务H的优先级实际上降到了任务L的优先级水平。因为任务H要一直等待直到任务L释放其占用的那个共享资源。由于任务M剥夺了任务L的CPU使用权使得任务H的情况更加恶化这样就相当于任务M的优先级高于任务H导致优先级翻转。  \quad 优先级翻转。简单地说就是高优先级任务必须等待低优先级任务的完成。 示例代码 
static void app_task_Low(void* pvParameters)
{uint32_t i;while(1){xSemaphoreTake(BinarySemaphore,portMAX_DELAY);  	//获取二值信号量printf([taskL]:access res begin\r\n);printf(low task Running!\r\n);for(i0;  i0x5000000;  i)              			//模拟低优先级任务占用二值信号量{//taskYIELD();                                	//发起任务调度}printf([taskL]:access res end\r\n);xSemaphoreGive(BinarySemaphore);                	//释放二值信号量vTaskDelay(1000);   //延时1s也就是1000个时钟节拍 }	
}   static void app_task_med(void* pvParameters)
{EventBits_t EventValue;for(;;){vTaskDelay(1000);   //延时1s也就是1000个时钟节拍 printf([taskM]:middle task running!\r\n);}
} static void app_task_high(void* pvParameters)
{BaseType_t errpdFALSE;for(;;){			vTaskDelay(500);    //延时500ms也就是500个时钟节拍   xSemaphoreTake(BinarySemaphore,portMAX_DELAY);  //获取二值信号量printf([taskH]:access res begin\r\n);printf(high task Running!\r\n);printf([taskH]:access res end\r\n);xSemaphoreGive(BinarySemaphore);                //释放信号量vTaskDelay(500);    //延时500ms也就是500个时钟节拍  }}2、解决方案 \quad 为了避免优先级翻转这个问题RTOS支持一种特殊的二进制信号量互斥信号量即互斥锁用它可以解决优先级翻转问题。  \quad 目前解决优先级翻转有许多种方法。其中普遍使用的有2种方法一种被称作优先级继承(priority inheritance)另一种被称作优先级天花板(priority ceilings)。 A. 优先级继承(priority inheritance) 优先级继承是指将低优先级任务的优先级提升到等待它所占有的资源的最高优先级任务的优先级。当高优先级任务由于等待资源而被阻塞时,此时资源的拥有者的优先级将会临时自动被提升以使该任务不被其他任务所打断从而能尽快的使用完共享资源并释放再恢复该任务原来的优先级别。 
B. 优先级天花板(priority ceilings) 优先级天花板是指将申请某资源的任务的优先级提升到可能访问该资源的所有任务中最高优先级任务的优先级。(这个优先级称为该资源的优先级天花板) 。这种方法简单易行 不必进行复杂的判断 不管任务是否阻塞了高优先级任务的运行 只要任务访问共享资源都会提升任务的优先级。 A和B的区别:优先级继承,只有当占有资源的低优先级的任务被阻塞时,才会提高占有资源任务的优先级而优先级天花板,不论是否发生阻塞,都提升。 在UCOSIII/FreeRTOS默认使用方案A详细如下图。