前言
本篇文章正式带大家开始学习什么是信号量,并且掌握信号量函数的基本使用方法,并且将和队列进行一个对比。
一、什么是信号量
FreeRTOS 中的信号量是一种用于任务间同步和互斥的机制。它允许任务在临界区保护资源的访问、线程间通信以及任务之间的同步操作。信号量可以用来保护共享资源,限制对资源的并发访问,以及进行任务间的事件通知。
二、信号量种类和对比
FreeRTOS 提供了两种类型的信号量:二值信号量(Binary Semaphore)和计数型信号量(Counting Semaphore)。
二值信号量(Binary Semaphore):
二值信号量是最简单的一种信号量,它只能有两个状态:0 和 1。它常用于互斥访问共享资源的情况,如保护共享数据结构、保证一次只有一个任务执行临界区等。任务可以通过获取或释放二值信号量来请求或释放对共享资源的访问。
创建二值信号量使用 xSemaphoreCreateBinary 函数,并通过 xSemaphoreGive 和 xSemaphoreTake 函数来释放和获取信号量。
计数型信号量(Counting Semaphore):
计数型信号量可以有多个状态值,允许多个任务同时访问共享资源,可以用于控制资源的可用性。计数型信号量常用于限制任务的并发执行数量,或者用于任务间的生产者-消费者模型等场景。
创建计数型信号量使用 xSemaphoreCreateCounting 函数,并通过 xSemaphoreGive 和 xSemaphoreTake 函数来增加和减少信号量的计数值。
在使用信号量时,任务通过获取信号量来尝试占用资源,并在未能获取时阻塞等待。一旦资源可用或条件满足,任务释放信号量,让其他任务可以获取资源或继续执行。这样可以确保对共享资源的安全性和正确性。
需要注意的是,使用信号量时要小心处理同步和互斥问题,以避免竞态条件和死锁。此外,信号量的使用应该遵循良好的软件设计原则,以避免过度使用和滥用信号量。
二种信号量的对比:
1.功能:
二值信号量(Binary Semaphore):只有两个状态,0 和 1。主要用于互斥访问共享资源的情况,保护共享数据结构,限制对资源的并发访问。一般用于排它性操作,尽量保持资源独占一个任务操作。
计数型信号量(Counting Semaphore):可以有多个状态值,用于控制资源的可用性。可用于限制并发执行数量、任务间的生产者-消费者模型等场景。
2.创建和初始化:
二值信号量:可以使用 xSemaphoreCreateBinary 函数创建,并使用 xSemaphoreGive 进行初始化,调用 xSemaphoreTake 来获取信号量。
计数型信号量:可以使用 xSemaphoreCreateCounting 函数创建,并使用 xSemaphoreGive 进行初始化,调用 xSemaphoreTake 来获取信号量。
3.值的范围:
二值信号量:具有两个状态,0 和 1。只能通过 xSemaphoreGive 和 xSemaphoreTake 将其值从 0 切换到 1 或者从 1 切换到 0。
计数型信号量:具有更大的范围,可以从 0 到一个设定的最大数值之间进行计数。
4.使用场景:
二值信号量:适用于互斥访问共享资源的场景,例如保护共享数据结构、保证一次只有一个任务执行临界区等。
计数型信号量:适用于控制并发执行数量的场景,或者用于任务间的生产者-消费者模型等
三、信号量和队列的区别
1.功能:
信号量:信号量是一种用于任务间同步和互斥的机制。它可以用于保护共享资源、限制并发访问、控制任务的执行顺序等。信号量具有两种类型:二值信号量和计数型信号量。
队列:队列是一种用于任务间传递数据的机制。它允许任务按照先进先出(FIFO)的顺序共享数据。任务可以将数据发送到队列,并从队列接收数据。队列的长度限制了可以在其中排队的数据项的数量。
2.数据传输方式:
信号量:信号量一般用于同步和互斥,不直接传输数据。通过获取和释放信号量来控制任务对资源的访问。二值信号量和计数型信号量的获取和释放操作可以用来表示任务的事件和计数。
队列:队列用于任务间传递数据。任务通过发送和接收操作将数据项从一个任务传递到另一个任务。发送操作将数据项复制到队列中,接收操作将数据项从队列中复制到接收任务的缓冲区中。
3.数据复制:
信号量:信号量在任务间的数据共享过程中通常不涉及数据复制。它们通常用于任务间对资源的访问控制,而不是实际的数据传输。二值信号量和计数型信号量是通过操作信号量的计数值来控制任务的行为。
队列:队列在任务间传递数据时涉及数据的复制。发送任务向队列发送数据项时,数据项的副本将存储在队列中。接收任务从队列接收数据项时,队列将数据项的副本复制到接收任务的缓冲区中。
4.用途:
信号量:信号量在需要任务进行同步和互斥的场景下非常有用。例如,保护共享资源、控制并发访问、任务的事件通知等。
队列:队列在需要任务间传递数据的场景下非常有用。例如,实现生产者-消费者模型、任务间的消息传递、任务间的数据通信等。
综上所述,信号量主要用于同步和互斥,控制任务对资源的访问。它们不直接传输数据,而是控制任务的行为。队列则用于任务间传递数据,按照先进先出的顺序共享数据项。选择使用信号量还是队列取决于需求,如任务间的数据传输、共享资源的访问控制,以及任务的同步需求。
四、信号量相关的函数
1.创建函数
创建二值信号量函数:
xSemaphoreCreateBinary 函数原型:
SemaphoreHandle_t xSemaphoreCreateBinary( void );
参数:无
返回值:SemaphoreHandle_t,一个二值信号量的句柄。
创建计数信号量函数:
xSemaphoreCreateCounting 函数原型:
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount );
参数:
uxMaxCount:信号量的最大计数值,即最多可以计数到多少。
uxInitialCount:信号量的初始计数值,通常为 0。
返回值:SemaphoreHandle_t,一个计数型信号量的句柄。
2.删除函数
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
参数:
xSemaphore:要删除的信号量的句柄。
要注意的是,删除一个信号量前,请确保没有任务正在等待该信号量,否则可能导致未定义的行为。在删除信号量之前,可以使用 uxSemaphoreGetCount 函数来获取当前信号量的计数值,以确保没有任务在等待。
3.获取和释放信号量函数
获取信号量函数:
xSemaphoreGive 函数原型:
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
参数:
xSemaphore:信号量的句柄。
返回值:如果成功释放信号量,则返回 pdPASS(1),否则返回 pdFAIL(0)。
中断中释放信号量函数:
xSemaphoreGiveFromISR 函数原型:
BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore, BaseType_t *pxHigherPriorityTaskWoken );
xSemaphore:信号量的句柄。
pxHigherPriorityTaskWoken:一个指向 BaseType_t 类型的变量的指针,用于指示在 ISR 中调用时是否唤醒了更高优先级的任务。
返回值:如果成功释放信号量,则返回 pdPASS(1),否则返回 pdFAIL(0)。
xSemaphoreGive 函数原型:
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
参数:
xSemaphore:信号量的句柄。
返回值:如果成功释放信号量,则返回 pdPASS(1),否则返回 pdFAIL(0)。
总结
本篇文章主要讲解了信号量的概念及API函数使用,大家看完后可以做个总结,并进行对应的实验。