【FreeRTOS】07 二值信号量、计数信号量

本节开始讲freeRTOS的信号量,先从最基本的二值信号量讲起,后面会讲解计数信号量,最后是信号量与临界段保护的区别。

1)什么是信号量

信号量是操作系统中用来解决资源共享和进程同步的一种方法。

举两个例子分别说明。

资源共享:假如我们系统里有一个串口,各个任务都要通过它发送数据,这个串口可以看作共享资源;如果不加控制地让任务随意访问串口,那么很可能会产生一个任务发送了一部分的数据时,串口资源被别的任务抢占,发送了新的数据;这样就出现了共享资源出错。我们希望在一个任务访问串口时,其他需要访问串口的任务暂时等待,直到占用串口的任务用完释放了串口资源,再轮到下一个任务访问。

信号量就是用来处理这类问题的,我们可以约定,在访问串口资源前,各任务先去竞争获取信号量,获取到信号量的任务可以访问串口,而其他任务被(信号量)阻塞;等到第一个任务访问完,它会释放信号量,让被阻塞的任务再去竞争,之后也是一样,获取到信号量的任务可以获得串口的使用权。这样我们就通过信号量解决了资源共享的问题。

任务同步:信号量用于任务同步的比较简单,假如多个任务间执行有先后要求,必须任务A完成了事件a,才能执行任务B中的函数b,那么我们可以在事件a之后释放一个信号量,而在b执行之前等待这个信号量,那么就保证了任务A、B中的事件同步执行。

信号量的作用有点像一些全局的标识量,我们也可以用全局变量来实现资源共享和任务同步,只是信号量还带有阻塞任务的功能,在操作系统里使用起来更为方便。

2)二值信号量

二值信号量顾名思义,它只有两种状态,被占用了可以看作0状态,未被占用可以看作1状态。

freeRTOS提供的二值信号量,主要由以下几个函数操作:

创建二值信号量:

SemaphoreHandle_t  xSemaphoreCreateBinary(void); //创建二值信号量

SemaphoreHandle_t  xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer ); //静态方式创建二值信号量,需用户指定信号量的地址空间

释放信号量:

BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore ); //释放信号量

BaseType_t xSemaphoreGiveFromISR( SemaphoreHandle_t xSemaphore, signed BaseType_t *pxHigherPriorityTaskWoken); //从中断中释放信号量,后一个参数的返回值表示是否有高优先级的任务已经就绪,如果有,则需要进行一次任务切换

获取信号量:

BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait ); //获取信号量,后一个参数为阻塞的超时时间

BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t  xSemaphore, BaseType_t *pxHigherPriorityTaskWoken); //在中断中获取信号量

我们以一个例子来说明如何使用二值信号量:

Task02用于创建信号量,并每隔1s发送一次信号量

【FreeRTOS】07 二值信号量、计数信号量_第1张图片

Task03等待信号量,一旦获取到,则打印提示:

【FreeRTOS】07 二值信号量、计数信号量_第2张图片

这个例子的运行结果如下,由打印的时间戳可以发现,Task02一旦发送信号量,Task03就立即进行了一次打印输出,与预期运行结果相同:

【FreeRTOS】07 二值信号量、计数信号量_第3张图片

如果想在中断中发送信号量,可以如下编写代码:

【FreeRTOS】07 二值信号量、计数信号量_第4张图片

其中选中的两行就是发送信号量,以及处理由高优先级任务就绪的情况。

3)计数信号量

freeRTOS中提供的计数信号量,与二值信号量有些区别,它可以记录发送信号量的次数,也就是说信号量的状态可以不仅仅为0和1,而是可以为自然数,也就是它可以表示一些资源数大于1的情况。

比如,我们有一块内存空间,共有10个字节,那么可以用一个初值为10的计数信号量来管理,每使用一个空间,信号量减1;每释放一个空间,信号量加1;信号量大于0,则表示有空间可用;信号量减到0,就表示没有空间可用了。

可以说二值信号量是计数信号量的一种特例,二值信号量是计数值最大为1的计数信号量。

freeRTOS提供的计数信号量,主要由以下几个函数操作:

创建计数信号量:

SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount ); //创建计数信号量,两个参数为:最大计数值、初始值

SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount, UBaseType_t  uxInitialCount, StaticSemaphore_t *  pxSemaphoreBuffer );//以静态形式创建计数信号量,需用户指定信号量的地址空间

释放信号量、获取信号量的函数,与二值信号量相同,这里就不重复了。

使用计数信号量时,要将以下宏定义改为1:

#define configUSE_COUNTING_SEMAPHORES 1

我们也以一个例子来说明如何使用计数信号量:

在Task02中定义计数信号量,并初始化为5,之后每隔1s发送一次信号量:

【FreeRTOS】07 二值信号量、计数信号量_第5张图片

Task03中等待信号量,并延时一小段时间,以方便观察打印信息:

【FreeRTOS】07 二值信号量、计数信号量_第6张图片

代码运行如下:

可以看到,在Task02创建完信号量并初始化为5后,Task03执行了5次;之后每隔1秒Task02发送一次信号量,Task03执行一次:

【FreeRTOS】07 二值信号量、计数信号量_第7张图片

4)信号量与临界段的关系

在信号量保护共享资源的用法中,我们发现,信号量所保护的共享资源,和上一节讲过的临界段有些类似,目的都是为了保护某一段代码不被意外访问。

不同的是,临界区的保护更加严格一些,它一旦保护,其它中断、任务都不会被执行,只有当前任务执行完才能退出;而信号量的保护不那么严格,它只限制那些等待这个信号量的任务,而与这个信号量无关的任务或中断,是不受影响的

好了,本节的内容就到这里了。下一节我们将继续讲解信号量,探讨一个与它有关的经典问题——优先级反转。

如果觉得有用可以关注作者微信号“小白白学电子”,在公众号可以找到代码和资料下载地址:

【FreeRTOS】07 二值信号量、计数信号量_第8张图片

 

你可能感兴趣的:(FreeRTOS,单片机)