FreeRTOS---------信号量

同志们,回想一下,你是否在裸机编程中这样使用过一个变量:用于标记某个事件是 否发生,或者标志一下某个东西是否正在被使用,如果是被占用了的或者没发生,我们就 不对它进行操作。

二值信号量被广泛应用于任务的同步,可以在中断 或者其他任务中释放二值信号量来控制另一个等待二值信号量的任务的运行。

如果两个任务同时等待一个二值信号量(当然平时肯定不能这么用),以任务的优先级高的为主,优先级低的无法获取二值信号量,如果两个任务的优先级相等则按照任务创建顺序依次分配。

计数信号量主要被应用于资源的访问数目限制,平时的话目前我想不到有什么应用场景。

信号量的概念

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

在多任务系统中,各任 务之间需要同步或互斥实现临界资源的保护,信号量功能可以为用户提供这方面的支持。 抽象的来讲,信号量是一个非负整数,所有获取它的任务都会将该整数减一(获取它 当然是为了使用资源),当该整数值为零时,所有试图获取它的任务都将处于阻塞状态。

通常一个信号量的计数值用于对应有效的资源数,表示剩下的可被占用的互斥资源数。其值的含义分两种情况:

  •  0:表示没有积累下来的释放信号量操作,且有可能有在此信号量上阻塞的任务。
  •  正值:表示有一个或多个释放信号量操作。

二值信号量

二值信号量既可以用于临界资源访问也可以用于同步功能。 二值信号量和互斥信号量(以下使用互斥量表示互斥信号量)非常相似,但是有一些细 微差别:互斥量有优先级继承机制,二值信号量则没有这个机制。这使得二值信号量更偏 向应用于同步功能(任务与任务间的同步或任务和中断间同步),而互斥量更偏向应用于 临界资源的访问。

用作同步时,信号量在创建后应被置为空,任务 1获取信号量而进入阻塞,任务 2在 某种条件发生后,释放信号量,于是任务 1获得信号量得以进入就绪态,如果任务 1的优 先级是最高的,那么就会立即切换任务,从而达到了两个任务间的同步。同样的,在中断 服务函数中释放信号量,任务1也会得到信号量,从而达到任务与中断间的同步。 还记得我们经常说的中断要快进快出吗,在裸机开发中我们经常是在中断中做一个标 记,然后在退出的时候进行轮询处理,这个就是类似我们使用信号量进行同步的,当标记 发生了,我们再做其他事情。

在 FreeRTOS 中我们用信号量用于同步,任务与任务的同步, 中断与任务的同步,可以大大提高效率。 可以将二值信号量看作只有一个消息的队列,因此这个队列只能为空或满(因此称为二 值),我们在运用的时候只需要知道队列中是否有消息即可,而无需关注消息是什么。


应用场景

二值信号量在任务与任务中同步的应用场景:假设我们有一个温湿度的传感器,假设 是 1s 采集一次数据,那么我们让他在液晶屏中显示数据出来,这个周期也是要 1s 一次的, 如果液晶屏刷新的周期是 100ms更新一次,那么此时的温湿度的数据还没更新,液晶屏根 本无需刷新,只需要在 1s后温湿度数据更新的时候刷新即可,否则 CPU 就是白白做了多 次的无效数据更新,CPU的资源就被刷新数据这个任务占用了大半,造成CPU资源浪费, 如果液晶屏刷新的周期是10s更新一次,那么温湿度的数据都变化了10次,液晶屏才来更 新数据,那拿这个产品有啥用,根本就是不准确的,所以,还是需要同步协调工作,在温 湿度采集完毕之后,进行液晶屏数据的刷新,这样子,才是最准确的,并且不会浪费 CPU 的资源。

同理,二值信号量在任务与中断同步的应用场景:我们在串口接收中,我们不知道啥 时候有数据发送过来,有一个任务是做接收这些数据处理,总不能在任务中每时每刻都在 任务查询有没有数据到来,那样会浪费CPU资源,所以在这种情况下使用二值信号量是很 好的办法,当没有数据到来的时候,任务就进入阻塞态,不参与任务的调度,等到数据到 来了,释放一个二值信号量,任务就立即从阻塞态中解除,进入就绪态,然后运行的时候 处理数据,这样子系统的资源就会很好的被利用起来。


二值信号量运作机制

创建信号量时,系统会为创建的信号量对象分配内存,并把可用信号量初始化为用户 自定义的个数, 二值信号量的最大可用信号量个数为1。 二值信号量获取,任何任务都可以从创建的二值信号量资源中获取一个二值信号量, 获取成功则返回正确,否则任务会根据用户指定的阻塞超时时间来等待其它任务/中断释放 信号量。在等待这段时间,系统将任务变成阻塞态,任务将被挂到该信号量的阻塞等待列 表中。 在二值信号量无效的时候,假如此时有任务获取该信号量的话,那么任务将进入阻塞状态,


计数信号量

二进制信号量可以被认为是长度为 1的队列,而计数信号量则可以被认为长度大于 1 的队列,信号量使用者依然不必关心存储在队列中的消息,只需关心队列是否有消息即可。

顾名思义,计数信号量肯定是用于计数的,在实际的使用中,我们常将计数信号量用于事件计数与资源管理。每当某个事件发生时,任务或者中断将释放一个信号量(信号量 计数值加 1),当处理事件时(一般在任务中处理),处理任务会取走该信号量(信号 量计数值减 1),信号量的计数值则表示还有多少个事件没被处理。此外,系统还有很多 资源,我们也可以使用计数信号量进行资源管理,信号量的计数值表示系统中可用的资源 数目,任务必须先获取到信号量才能获取资源访问权,当信号量的计数值为零时表示系统 没有可用的资源,但是要注意,在使用完资源的时候必须归还信号量,否则当计数值为 0 的时候任务就无法访问该资源了。 计数型信号量允许多个任务对其进行操作,但限制了任务的数量。当任务数量最大时,只有其中一个任务释放了这个资源,后面的任务才能对这个资源进行访问。

计数信号量可以用于资源管理,允许多个任务获取信号量访问共享资源,但会限制任 务的最大数目。访问的任务数达到可支持的最大数目时,会阻塞其他试图获取该信号量的 任务,直到有任务释放了信号量。这就是计数型信号量的运作机制。


常用函数

1.创建二值信号量xSemaphoreCreateBinary()

xSemaphoreCreateBinary()用于创建一个二值信号量,并返回一个句柄。其实二值信号 量和互斥量都共同使用一个类型SemaphoreHandle_t 的句柄。

该句柄的原 型是一个 void 型的指针。使用该函数创建的二值信号量是空的,在使用函数 xSemaphoreTake()获取之前必须先调用函数 xSemaphoreGive()释放后才可以获取。

SemaphoreHandle_t BinarySem_Handle =NULL;
BinarySem_Handle = xSemaphoreCreateBinary();	//这样就算是成功创建了一个二值信号量

xSemaphoreTake(BinarySem_Handle,portMAX_DELAY); //获取二值信号量直到有任务或者中断释放了二值信号量

xSemaphoreGive( BinarySem_Handle );//给出二值信号量

2.创建计数信号量xSemaphoreCreateCounting()

xSemaphoreCreateCounting ()用于创建一个计数信号量。

SemaphoreHandle_t CountSem_Handle =NULL;
CountSem_Handle = xSemaphoreCreateCounting(5,1);//这样就算是创建了一个计数信号量,5是这个计数信号量的最大值,1是这个计数信号量被创建的时候的初始值。


xSemaphoreTake(CountSem_Handle,	0);//获取一个计数信号量,第一个参数为信号量句柄,第二个参数为等待时间


 xSemaphoreGive(CountSem_Handle);//给出计数信号量 

3.信号量删除函数vSemaphoreDelete()

vSemaphoreDelete()用于删除一个信号量,包括二值信号量,计数信号量,互斥量和递 归互斥量。如果有任务阻塞在该信号量上,那么不要删除该信号量。

4.信号量释放函数xSemaphoreGive()

xSemaphoreGive()是一个用于释放信号量的宏。释放的信号量对象必须是已 经被创建的,可以用于二值信号量、计数信号量、互斥量的释放,但不能释放由函数 xSemaphoreCreateRecursiveMutex()创建的递归互斥量。此外该函数不能在中断中使用。

   xSemaphoreGiveFromISR()(中断)

用于释放一个信号量,带中断保护。被释放的信号量可以是二进制信号量和计数信号 量。和普通版本的释放信号量API函数有些许不同,它不能释放互斥量,这是因为互斥量 不可以在中断中使用,互斥量的优先级继承机制只能在任务中起作用,而在中断中毫无意 义。

5.信号量获取函数xSemaphoreTake()

xSemaphoreTake()函数用于获取信号量,不带中断保护。获取的信号量对象可以是二 值信号量、计数信号量和互斥量,但是递归互斥量并不能使用这个API函数获取。其实获 取信号量是一个宏,真正调用的函数是xQueueGenericReceive ()。该宏不能在中断使用, 而是必须由具体中断保护功能的xQueueReceiveFromISR()版本代替。

从该宏定义可以看出释放信号量实际上是一次消息出队操作,阻塞时间由用户指定 xBlockTime,当有任务试图获取信号量的时候,当且仅当信号量有效的时候,任务才能读 获取到信号量。如果信号量无效,在用户指定的阻塞超时时间中,该任务将保持阻塞状态 以等待信号量有效。当其它任务或中断释放了有效的信号量,该任务将自动由阻塞态转移 为就绪态。当任务等待的时间超过了指定的阻塞时间,即使信号量中还是没有可用信号量, 任务也会自动从阻塞态转移为就绪态。

   xSemaphoreTakeFromISR()(中断)

xSemaphoreTakeFromISR()是函数 xSemaphoreTake()的中断保护版本,用于获取信号量,是 一个不带阻塞机制获取信号量的函数,获取对象必须由是已经创建的信号量,信号量类型 可以是二值信号量和计数信号量,它与 xSemaphoreTake()函数不同,它不能用于获取互斥量,因为互斥量不可以在中断中使用,并且互斥量特有的优先级继承机制只能在任务中起 作用,而在中断中毫无意义。

  • 51
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值