什么是信号量
信号量是操作系统中重要的一部分,信号量一般用来进行资源管理和任务同步,FreeRTOS中信号量又分为二值信号量,计数型信号量,互斥信号量和递归互斥信号量。不同信号量其应用场景不同,但有些应用场景是可以互换着使用的。
信号量的重要作用其中就有任务的同步,用于任务与任务之间的同步,用于中断与任务之间的同步。在执行中断服务函数的时候可以通过向任务发送信号量来通知它所期待的事情发生了,当退出中断服务函数以后在任务调度器的调度下同步的任务回去执行。
在编写中断服务函数的时候一定是要快进快出的,中断服务函数中一般都不能放入太多的代码,否则就会影响系统的实时性。在裸机编写代码的时候一般都只是在中断服务函数中打一个标记,然后在其他的地方根据标记的值来进行具体的处理过程。而这些在RTOS中就可以借助信号量来进行实现。
当中断发生的时候就释放信号量,但在中断服务函数中不做具体的处理。具体的处理过程做成一个任务,这个任务会获取信号量,如果获取到信号量就说明中断发生了,需要进行相应的处理,这样做的好处就是中断的时间会非常短。当然任务与任务之间也可以通过信号量来进行同步。
二值信号量
二值信号量通常用于互斥访问或同步。互斥信号量拥有优先级继承机制,二值信号量没有优先级继承。因此二值信号量更适合用于同步任务与中断或者任务与任务,而互斥信号量更适合用于简单的互斥访问。
和队列一样,信号量API函数允许设置一个阻塞时间,阻塞时间是当任务获取信号量的时候由于信号量无效从而导致任务进入阻塞态的最大时钟节拍数。如果多个任务同时阻塞在同一个信号量上的话那么哪个任务的优先级高,哪个任务就优先获得信号量,这样当信号量有效的时候高优先级的任务就会解除阻塞状态。
二值信号量其实就是只有一个队列项的队列,这个特殊的队列要么是满的,要么是空的,所以其被称之为二值信号量。只需要知道这个队列项是空的还是满的,就可以利用这个机制来完成任务与中断之间的同步。
在实际应用中通常会使用一个任务来处理MCU的某个外设,比如在网络应用中,一般最简单的方法就是使用一个任务去轮询的查询MCU的ETH外设是否有数据,当有数据的时候就处理这个网络数据。这样使用轮询的方式是很浪费CPU资源的,而且也阻止了其他任务的运行。最理想的方法就是当没有网络数据的时候该任务进入阻塞态,把CPU让给其他任务,当有数据的时候采取执行。
现在通过信号量就可完成这样的目标任务通过获取二值信号量来判断是否有网络数据,没有的话就进入阻塞状态,当网络中断发生了,就会在中断服务函数中释放信号量来通知任务可以去处理数据了。
获取二值信号量的过程
图14.2.1.1中任务Task通过函数xSemaphoreTake()获取信号量,但是此时二值信号量无效,所以任务Task进入阻塞状态
此时中断发生了,在中断服务函数中通过函数xSemaphoreGiveFromISR()释放信号量,因此信号量变为有效
由于信号量已经有效了,所以任务Task获取信号量成功,任务从阻塞态解除,开始执行相关的处理过程
任务再次进入阻塞态
由于任务函数一般都是一个大循环,所以在任务做完相关的处理之后就会再次调用函数xSemaphoreTake()获取信号量。在执行完第三步以后二值信号量就已经变为无效的了,所以任务将再次进入阻塞状态,和以第一步一样直到中断再次发生并调用函数xSemaphoreGiveFromISR()释放信号量
二值信号量相关函数
二值信号量创建函数
此函数创建的二值信号量的话信号量所需的RAM是由FreeRTOS的内存管理部分来动态分配的。此函数创建好的二值信号量默认是空的,也就是说刚创建好的二值信号量使用函数xSemaphoreTake()是获取不到的。
/* 创建二值信号量 */
SemaphoreHandle_t xSemaphoreCreateBinary( void )
参数
无
返回值
NULL: 二值信号量创建失败
其他值: 创建成功的二值信号量的句柄
释放信号量函数
此函数用于释放二值信号量,计数型信号量或互斥信号量。
BaseType_t xSemaphoreGive( xSemaphore )
参数
xSemaphore: 要释放的信号量句柄
返回值
pdPASS: 释放信号量成功
errQUEUE_FULL: 释放信号量失败。
xSemaphoreGiveFromISR这是它的中断版本
BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore,
BaseType_t * pxHigherPriorityTaskWoken)
参数
xSemaphore: 要释放的信号量句柄
pxHigherPriorityTaskWoken: 标记退出此函数以后是否进行任务切换,这个变量的值由标记退出此函数以后是否进行任务切换,这个变量的值由三个函数来设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为pdTRUE的时候在退出中断服务函数之前一定要进行一次任务切换
返回值
pdPASS: 释放信号量成功
errQUEUE_FULL: 释放信号量失败。
获取信号量函数
此函数用于获取二值信号量,计数型信号量或互斥信号量
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,
ickType_t xBlockTime)
参数
xSemaphore: 要获取的信号量句柄
xBlockTime: 阻塞时间
返回值
pdPASS: 获取信号量成功
pdFALSE: 超时,获取信号量失败。
xSemaphoreTakeFromISR是他的中断版本
BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,
BaseType_t * pxHigherPriorityTaskWoken)
参数
xSemaphore: 要获取的信号量句柄
xBlockTipxHigherPriorityTaskWoken:标记退出此函数以后是否进行任务切换,这个变量的值由标记退出此函数以后是否进行任务切换,这个变量的值由三个函数来设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为pdTRUE的时候在退出中断服务函数之前一定要进行一次任务切换
返回值
pdPASS: 获取信号量成功
pdFALSE: 超时,获取信号量失败。