十八、信号量
创始人
2025-05-31 22:58:32
0

文章目录

  • 1、基本概念及分类(本质还是队列)
    • (1)二值信号量
    • (2)计数信号量
    • (3)互斥信号量
    • (4)递归信号量
  • 2、使用场景:用于表示资源的个数
  • 3、信号量的创建
    • (1)二值信号量的创建
    • (2)计数信号量的创建
  • 4、信号量的删除
  • 5、信号量的获取
    • (1)获取流程概述
    • (2)API函数
      • 1)xSemaphoreTake
      • 2)xSemaphoreTakeFromISR
  • 6、信号量的释放
    • (1)释放流程概述
    • (2)API函数
      • 1)xSemaphoreGive
      • 2)xSemaphoreGiveFromISR

1、基本概念及分类(本质还是队列)

信号量是一种实现任务间通信的机制,可以实现任务之间同步或临界资源的互斥访问, 常用于协助一组相互竞争的任务来访问临界资源。

抽象的来说,信号量是一个非负整数,所有获取它的任务都会将该整数减一,所有获取它的任务都将处于阻塞状态。
通常一个信号量的计数值用于对应有效的资源数,表示剩下的可被占用的互斥资源数,具体含义如下:
​ 0:表示没有积累下来的释放信号量操作,且有可能有在此信号量上的阻塞的任务。
​ 正值:表示有一个或多个释放信号量操作。
在这里插入图片描述

即其相当于是一个特殊的队列,还是具备休眠唤醒功能。

(1)二值信号量

二值信号量既可以用于临界资源访问也可以用于同步功能。

用作同步时:
信号量在被创建后应被置为空,任务一获取信号量而进入阻塞,任务二在某种条件发生后,释放信号量,于是任务一获得信号量得以进入就绪态,若任务一的优先级是最高的,那么就会立即切换任务,从而达到两个任务间的同步。
在这里插入图片描述

(2)计数信号量

计数信号量常用于事件计数与资源管理。

1)用于事件计数时:
​ 每当某个事件发生时,任务或者中断将释放一个信号量(信号量计数值加 1),当处理被事件时(一般在任务中处理),处理任务会取走该信号量(信号量计数值减 1),信号量的计数值则表示还有多少个事件没被处理。

2)用于资源管理时:
​ 我们也可以使用计数信号量进行资源管理,信号量的计数值表示系统中可用的资源数目,任务必先获取到信号量才能获得资源访问权,当信号量的计数值为零时表示系统没有可用的资源。所以的注意使用完资源的时候必须归还信号量。

即此时计数型信号量允许多个任务对其进行操作,但限制了任务的数量。
在这里插入图片描述

(3)互斥信号量

互斥信号量为特殊的二值信号量,由于其特有的优先级继承机制,从而使它更适用于简单互锁,即保护临界资源。

临界资源:指任何时刻只能被一个任务访问的资源。
用作互斥时:
​ 信号量创建后可用信号量个数应该是满的,任务在需要使用临界资源时,先获取互斥信号量,使其变空,这样其他任务需要使用临界资源时就会因为无法获取信号量而进入阻塞,从而保证了临界资源的安全。

在操作系统中,使用任务量很多时候是为了给临界资源建立一个标志,信号量表示了该临界资源被占用情况。

这样当一个任务在访问临界资源的时候,就会先对目标资源信息进行查询,从而了解资源被占用的情况后再做处理,即临界资源得到了有效的保护。

(4)递归信号量

递归,即可以重复获取调用。
即对于已经获取递归互斥量的任务可以重复获取该递归互斥量,该任务拥有递归信号量的所有权。

但任务成功获取多少次递归互斥量,就要返还几次在此前递归互斥量都处于无效状态,其他任务无法获取,只有持有递归信号量的任务才能获取和释放。

2、使用场景:用于表示资源的个数

二值信号量:即信号量资源被获取了,信号量值就是0,信号量资源被释放,信号量值就是1。

在多任务系统中,经常会使用二值信号量:

​ 当任务获取信号量时,因为此时未发生特定事件,信号量为空,任务会进入阻塞状态;当事件的条件满足后,任务/中断会释放信号量,告知这个事件发生了,任务获得信号量便被唤醒去执行对应的操作,任务执行完后并不需要归还信号量。

1)在任务与任务中同步的应用场景
​ 其原理同如上所述。

2)在任务与中断同步的应用场景
​ 如在串口接收中,有一个任务就是接收数据处理,当没有数据到来的时候,任务进入阻塞态,不参与任务的调度;等到数据到了,释放一个二值信号量,任务就立刻从阻塞态中进入就绪态。

3、信号量的创建

(1)二值信号量的创建

在信号量创建之后,变量 uxMessageWaiting 的值为0,这说明当前信号量处于无效状态,此时的信号量是无法被获取的,在获取信号之前,应先释放一个信号量。(重点)即创建后二值信号量默认为无效的。

/* 创建一个二进制信号量,返回它的句柄。
* 此函数内部会分配信号量结构体
* 返回值: 返回句柄,非 NULL 表示成功
*/
SemaphoreHandle_t xSemaphoreCreateBinary( void );
/* 创建一个二进制信号量,返回它的句柄。
* 此函数无需动态分配内存,所以需要先有一个 StaticSemaphore_t 结构体,并传入它的指
针
* 返回值: 返回句柄,非 NULL 表示成功
*/
SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer );

(2)计数信号量的创建

创建的计数信号量只有消息队列控制块结构体存储空间而没有消息存储空间 ,这一点与二值信号量一致

/* 创建一个计数型信号量,返回它的句柄。
* 此函数内部会分配信号量结构体
* uxMaxCount: 最大计数值
* uxInitialCount: 初始计数值
* 返回值: 返回句柄,非 NULL 表示成功
*/
SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);
/* 创建一个计数型信号量,返回它的句柄。
* 此函数无需动态分配内存,所以需要先有一个 StaticSemaphore_t 结构体,并传入它的指
针
* uxMaxCount: 最大计数值
* uxInitialCount: 初始计数值
* pxSemaphoreBuffer: StaticSemaphore_t 结构体指针
* 返回值: 返回句柄,非 NULL 表示成功
*/
SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount,UBaseType_t uxInitialCount,StaticSemaphore_t *pxSemaphoreBuffer );

4、信号量的删除

删除信号量的过程实质为删除消息队列的过程

/*
* xSemaphore: 信号量句柄,你要删除哪个信号量
*/
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore )

5、信号量的获取

获取信号量实际上是一次消息出队操作,阻塞时间由用户指定xBlockTime,当有任务试图获取信号量的时候,当且仅当信号量有效的时候,任务才能读获取到信号量。
如果信号量无效,在用户指定的阻塞超时时间中,该任务将保持阻塞状态以等待信号量有效。

获取信号量的过程:
1)如果有可用信号量,控制块结构体成员 uxMessageWaiting 就会减 1,然后返回获取成功信息(pdPASS);
2)如果信号量无效并且阻塞时间为 0,则返回错误代码(errQUEUE_EMPTY);
3)如果信号量无效并且用户指定了阻塞时间,则任务会因为等待信号量而进入阻塞状态,任务会被挂接到延时列表中。直到超过阻塞时间后才会变为就绪态。

(1)获取流程概述

在这里插入图片描述

(2)API函数

1)xSemaphoreTake

BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);

在这里插入图片描述

2)xSemaphoreTakeFromISR

BaseType_t xSemaphoreTakeFromISR( SemaphoreHandle_t xSemaphore,BaseType_t*pxHigherPriorityTaskWoken)

在这里插入图片描述

6、信号量的释放

实际上可以把释放信号量看为一次入队操作,且不允许入队阻塞,即阻塞时间为0。

释放信号量的过程如下:
​ 若信号量未满,控制块结构体成员 uxMessageWaiting 就会加 1,然后判断是否有阻塞的任务,如果有的话就会恢复阻塞的任务,然后返回成功信息(pdPASS);如果信号量已满,则返回错误代码(err_QUEUE_FULL)

(1)释放流程概述

在这里插入图片描述

(2)API函数

1)xSemaphoreGive

BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );

在这里插入图片描述

2)xSemaphoreGiveFromISR

 SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken);

在这里插入图片描述

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...