标签:
FreeRTOS的信号量包括二进制信号量、计数信号量、互斥信号量(以后简称互斥量)和递归互斥信号量(以后简称递归互斥量)。我们可以把互斥量和递归互斥量看成特殊的信号量。
信号量API函数实际上都是宏,它使用现有的队列机制。这些宏定义在semphr.h文件中。如果使用信号量或者互斥量,需要包含semphr.h头文件。
二进制信号量、计数信号量和互斥量信号量的创建API函数是独立的,但是获取和释放API函数都是相同的;递归互斥信号量的创建、获取和释放API函数都是独立的。
SemaphoreHandle_t xSemaphoreCreateBinary( void );
这个函数用于创建一个二进制信号量。二进制信号量要么有效要么无效,这也是为什么叫做二进制的原因。
新创建的信号量处于无效状态,这意味着使用API函数xSemaphoreTake()获取信号之前,需要先给出信号。
二进制信号量和互斥量非常相似,但也有细微的区别:互斥量具有优先级继承机制,二进制信号量没有这个机制。这使得二进制信号量更适合用于同步(任务之间或者任务和中断之间),互斥量更适合互锁。
一旦获得二进制信号量后不需要恢复,一个任务或中断不断的产生信号,而另一个任务不断的取走这个信号,通过这样的方式来实现同步。
低优先级任务拥有互斥量的时候,如果另一个高优先级任务也企图获取这个信号量,则低优先级任务的优先级会被临时提高,提高到和高优先级任务相同的优先级。这意味着互斥量必须要释放,否则高优先级任务将不能获取这个互斥量,并且那个拥有互斥量的低优先级任务也永远不会被剥夺,这就是操作系统中的优先级翻转。
互斥量和二进制信号量都是SemaphoreHandle_t类型,并且可以用于任何具有这类参数的API函数中。
SemaphoreHandle_t xSemaphore; void vATask( void * pvParameters ) { /* 创建信号量 */ xSemaphore = xSemaphoreCreateBinary(); if( xSemaphore == NULL ) { /* 因堆栈不足,信号量创建失败,这里进行失败处理*/ } else { /* 信号量可以使用。信号量句柄存储在变量xSemahore中。 如果在这里调用API函数xSemahoreTake()来获取信号量, 则必然是失败的,因为创建的信号量初始是无效(空)的。*/ } }
SemaphoreHandle_t xSemaphoreCreateCounting ( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount )
创建计数信号量,计数信号量通常用于以下两种情况:
NULL表示信号量创建失败,否则返回信号量句柄。
void vATask( void * pvParameters ) { xSemaphoreHandle xSemaphore; // 必须先创建信号量,才能使用它 // 信号量可以计数的最大值为10,计数初始值为0. xSemaphore = xSemaphoreCreateCounting( 10, 0 ); if( xSemaphore != NULL ) { // 信号量创建成功 // 现在可以使用信号量了。 } }
SemaphoreHandle_t xSemaphoreCreateMutex( void )
创建互斥量。可以使用API函数xSemaphoreTake()和xSemaphoreGive()访问互斥量,但是绝不可以用xSemaphoreTakeRecursive()和xSemaphoreGiveRecursive()访问。
二进制信号量和互斥量非常相似,但也有细微的区别:互斥量具有优先级继承机制,二进制信号量没有这个机制。这使得二进制信号量更适合用于同步(任务之间或者任务和中断之间),互斥量更适合互锁。
一旦获得二进制信号量后不需要恢复,一个任务或中断不断的产生信号,而另一个任务不断的取走这个信号,通过这样的方式来实现同步。
低优先级任务拥有互斥量的时候,如果另一个高优先级任务也企图获取这个信号量,则低优先级任务的优先级会被临时提高,提高到和高优先级任务相同的优先级。这意味着互斥量必须要释放,否则高优先级任务将不能获取这个互斥量,并且那个拥有互斥量的低优先级任务也永远不会被剥夺,这就是操作系统中的优先级翻转。
互斥量和二进制信号量都是SemaphoreHandle_t类型,并且可以用于任何具有这类参数的API函数中。
NULL表示信号量创建失败,否则返回信号量句柄。
xSemaphoreHandle xSemaphore; voidvATask( void * pvParameters ) { // 互斥量在未创建之前是不可用的 xSemaphore = xSemaphoreCreateMutex(); if( xSemaphore != NULL ) { // 创建成功 // 在这里可以使用这个互斥量了 } }
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )
用于创建递归互斥量。被创建的互斥量可以被API函数xSemaphoreTakeRecursive()和xSemaphoreGiveRecursive()使用,但不可以被API函数xSemaphoreTake()和xSemaphoreGive()使用。
递归类型的互斥量可以被拥有者重复获取。拥有互斥量的任务必须调用API函数xSemaphoreGiveRecursive()将拥有的递归互斥量全部释放后,该信号量才真正被释放。比如,一个任务成功获取同一个互斥量5次,那么这个任务要将这个互斥量释放5次之后,其它任务才能获取到它。
递归互斥量具有优先级继承机制,因此任务获得一次信号后必须在使用完后做一个释放操作。
互斥量类型信号不可以用在中断服务例程中。
NULL表示互斥量创建失败,否则返回互斥量句柄。
xSemaphoreHandle xMutex; void vATask( void * pvParameters ) { // 互斥量未创建前是不能被使用的 xMutex = xSemaphoreCreateRecursiveMutex(); if( xMutex != NULL ) { // 创建成功 // 在这里创建互斥量 } }
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
删除信号量。如果有任务阻塞在这个信号量上,则这个信号量不要删除。
xSemaphore:信号量句柄
xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait)
获取信号量。信号量必须是通过API函数xSemaphoreCreateBinary()、xSemaphoreCreateCounting()和xSemaphoreCreateMutex()预先创建过的。注意,递归互斥量类型信号量不能使用该函数、不用在中断服务程序中使用该函数。
成功获取到信号量返回pdTRUE,否则返回pdFALSE。
SemaphoreHandle_t xSemaphore = NULL; /*这个任务创建信号量 */ void vATask( void * pvParameters ) { /*创建互斥型信号量,用于保护共享资源。*/ xSemaphore = xSemaphoreCreateMutex(); } /* 这个任务使用信号量 */ void vAnotherTask( void * pvParameters ) { /* ... 做其它事情. */ if( xSemaphore != NULL ) { /*如果信号量无效,则最多等待10个系统节拍周期。*/ if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE ) { /*到这里我们获取到信号量,现在可以访问共享资源了*/ /* ... */ /* 完成访问共享资源后,必须释放信号量*/ xSemaphoreGive( xSemaphore ); } else { /* 没有获取到信号量,这里处理异常情况。*/ } } }
xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore, signedBaseType_t *pxHigherPriorityTaskWoken)
API函数xSemaphoreTake()的另一版本,用于中断服务程序。
信号量成功获取返回pdTRUE,否则返回pdFALSE。
xSemaphoreTakeRecursive(SemaphoreHandle_t xMutex, TickType_t xTicksToWait );
获取递归互斥信号量。互斥量必须是通过API函数xSemaphoreCreateRecursiveMutex()创建的类型。
文件FreeRTOSConfig.h中的宏configUSE_RECURSIVE_MUTEXES必须设置成1,此函数才有效。
已经获取递归互斥量的任务可以重复获取该递归互斥量。使用xSemaphoreTakeRecursive() 函数成功获取几次递归互斥量,就要使用xSemaphoreGiveRecursive()函数返还几次,在此之前递归互斥量都处于无效状态。比如,某个任务成功获取5次递归互斥量,那么在它没有返还5次该递归互斥量之前,这个互斥量对别的任务无效。
成功获取递归互斥量返回pdTURE,否则返回pdFALSE。
SemaphoreHandle_t xMutex = NULL; // 这个任务创建互斥量 void vATask( void *pvParameters ) { // 这个互斥量用于保护共享资源 xMutex =xSemaphoreCreateRecursiveMutex(); } //这个任务使用互斥量 void vAnotherTask( void *pvParameters ) { // ... 做其它事情. if( xMutex != NULL ) { // 如果互斥量无效,则最多等待10系统时钟节拍周期. if(xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE ) { // 到这里我们成功获取互斥量并可以访问共享资源了 // ... // 由于某种原因,某些代码需要在一个任务中多次调用API函数 // xSemaphoreTakeRecursive()。当然不会像本例中这样连续式 //调用,实际代码会有更加复杂的结构 xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); // 我们获取一个互斥量三次,所以我们要将这个互斥量释放三次 //它才会变得有效。再一次说明,实际代码可能会更加复杂。 xSemaphoreGiveRecursive( xMutex ); xSemaphoreGiveRecursive( xMutex ); xSemaphoreGiveRecursive( xMutex ); // 到这里,这个共享资源可以被其它任务使用了. } else { // 处理异常情况 } } }
xSemaphoreGive(SemaphoreHandle_t xSemaphore )
用于释放一个信号量。信号量必须是API函数xSemaphoreCreateBinary()、xSemaphoreCreateCounting()或xSemaphoreCreateMutex() 创建的。必须使用API函数xSemaphoreTake()获取这个信号量。
这个函数绝不可以在中断服务例程中使用,可以使用带中断保护版本的API函数xSemaphoreGiveFromISR()来实现相同功能。
这个函数不能用于使用API函数xSemaphoreCreateRecursiveMutex()所创建的递归互斥量。
信号量释放成功返回pdTRUE,否则返回pdFALSE。
SemaphoreHandle_t xSemaphore = NULL; voidvATask( void * pvParameters ) { // 创建一个互斥量,用来保护共享资源 xSemaphore = xSemaphoreCreateMutex(); if( xSemaphore != NULL ) { if( xSemaphoreGive( xSemaphore ) != pdTRUE ) { //我们希望这个函数调用失败,因为首先要获取互斥量 } // 获取信号量,不等待 if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) ) { // 现在我们拥有互斥量,可以安全的访问共享资源 if( xSemaphoreGive( xSemaphore ) != pdTRUE ) { //我们不希望这个函数调用失败,因为我们必须 //要释放已获取的互斥量 } } } }
xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore, signed BaseType_t *pxHigherPriorityTaskWoken )
释放信号量。是API函数xSemaphoreGive()的另个版本,用于中断服务程序。信号量必须是通过API函数xSemaphoreCreateBinary()或xSemaphoreCreateCounting()创建的。这里没有互斥量,是因为互斥量不可以用在中断服务程序中。
xSemaphore:信号量句柄
pxHigherPriorityTaskWoken:如果*pxHigherPriorityTaskWoken为pdTRUE,则需要在中断退出前人为的经行一次上下文切换。从FreeRTOS V7.3.0开始,该参数为可选参数,并可以设置为NULL。
成功释放信号量返回pdTURE,否则返回errQUEUE_FULL。
#define LONG_TIME 0xffff #define TICKS_TO_WAIT 10 SemaphoreHandle_t xSemaphore = NULL; /* Repetitive task. */ void vATask( void * pvParameters ) { /* 我们使用信号量同步,所以先创建一个二进制信号量.必须确保 在创建这个二进制信号量之前,中断不会访问它。*/ xSemaphore = xSemaphoreCreateBinary(); for( ;; ) { /* 我们希望每产生10次定时器中断,任务运行一次。*/ if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE ) { /* 到这里成功获取到信号量*/ ... /* 我们成功执行完一次,由于这是个死循环,所以任务仍会 阻塞在等待信号量上。信号量由ISR释放。*/ } } } /* 定时器 ISR */ void vTimerISR( void * pvParameters ) { static unsigned char ucLocalTickCount = 0; static signed BaseType_txHigherPriorityTaskWoken; /*定时器中断发生 */ ...执行其它代码 /*需要vATask() 运行吗? */ xHigherPriorityTaskWoken = pdFALSE; ucLocalTickCount++; if( ucLocalTickCount >= TICKS_TO_WAIT ) { /* 释放信号量,解除vATask任务阻塞状态 */ xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken ); /* 复位计数器 */ ucLocalTickCount = 0; } /* 如果 xHigherPriorityTaskWoken 表达式为真,需要执行一次上下文切换*/ portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); }
xSemaphoreGiveRecursive(SemaphoreHandle_t xMutex )
释放一个递归互斥量。互斥量必须是使用 API函数xSemaphoreCreateRecursiveMutex()创建的。文件FreeRTOSConfig.h中宏configUSE_RECURSIVE_MUTEXES必须设置成1本函数才有效。
如果递归互斥量释放成功,返回pdTRUE。
见“8 获取递归互斥量”。
TaskHandle_t xSemaphoreGetMutexHolder( SemaphoreHandle_t xMutex );
返回互斥量持有任务的句柄(如果有的话),互斥量由参数xMutex指定。
如果调用此函数的任务持有互斥量,那么可以可靠的返回任务句柄,但是如果是别的任务持有互斥量,则不总可靠。
文件FreeRTOSConfig.h中宏configUSE_MUTEXES必须设置成1本函数才有效。
返回互斥量持有任务的句柄。如果参数xMutex不是互斥类型信号量或者虽然互斥量有效但这个互斥量不被任何任务持有则返回NULL。
这是FreeRTOS基础篇的最后一篇博文,到这里我们已经可以移植、熟练使用FreeRTOS了。如果想知道FreeRTOS背后的运行机制,这些是远远不够的,下面要走的路还会很长。
FreeRTOS系列第20篇---FreeRTOS信号量API函数
标签:
原文地址:http://blog.csdn.net/zhzht19861011/article/details/50920336