18.1 信号量的基本概念

信号量(Semaphore)是一种实现任务间通信的机制,可以实现任务之间同步或临界资源的互斥访问, 常用于协助一组相互竞争的任务来访问临界资源。在多任务系统中,各任务之间需要同步或互斥实现临界资源的保护,信号量功能可以为用户提供这方面的支持。
信号量就是个非负整数:获取会-1,当为0的时候阻塞。一般分为2种情况。

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

18.1.1 二值信号量

二值信号量既可以用于临界资源访问也可以用于同步功能。
二级信号量和互斥信号的区别:
互斥量有优先级继承机制, 二值信号量则没有这个机制。这使得二值信号量更偏向应用于同步功能(任务与任务间的同步或任务和中断间同步), 而互斥量更偏向应用于临界资源的访问。

同步功能:
信号量在创建后应被置为空,任务 1 获取信号量而进入阻塞,任务 2 在某种条件发生后,释放信号量,于是任务 1 获得信号量得以进入就绪态,如果任务 1 的优先级是最高的,那么就会立即切换任务,从而达到了两个任务间的同步。同样的,在中断服务函数中释放信号量, 任务 1 也会得到信号量,从而达到任务与中断间的同步。
任务2执行->获取信号量->任务1阻塞->任务2释放->任务1(优先级最高)执行

中断:
在裸机开发中我们经常是在中断中做一个标记,然后在退出的时候进行轮询处理,这个就是类似我们使用信号量进行同步的,当标记发生了,我们再做其他事情。在 FreeRTOS 中我们用信号量用于同步,任务与任务的同步,中断与任务的同步,可以大大提高效率。

这个相当于只有一个消息的队列,而且使用的时候不必关心消息是什么。

18.1.2 计数信号量

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

计数信号量的功能:
计数信号量:用来计数的。
每当某个事件发生时,任务或者中断将释放一个信号量(信号量计数值加 1),当处理被事件时(一般在任务中处理),处理任务会取走该信号量(信号量计数值减 1),信号量的计数值则表示还有多少个事件没被处理。
信号量的计数值表示系统中可用的资源数目,任务必须先获取到信号量才能获取资源访问权,当信号量的计数值为零时表示系统没有可用的资源。
计数型信号量允许多个任务对其进行操作,但限制了任务的数量。

18.1.3 互斥信号量

互斥信号量其实是特殊的二值信号量,由于其特有的优先级继承机制从而使它更适用于简单互锁,也就是保护临界资源。
使用信号量的很多时候是为了给临界资源建立一个标志,信号量表示了该临界资源被占用情况。这样,当一个任务在访问临界资源的时候,就会先对这个资源信息进行查询,从而在了解资源被占用的情况之后,再做处理,从而使得临界资源得到有效的保护。

18.1.4 递归信号量

递归信号量,见文知义,递归嘛,就是可以重复获取调用的,本来按照信号量的特性,每获取一次可用信号量个数就会减少一个,但是递归则不然, 对于已经获取递归互斥量的,任务可以重复获取该递归互斥量, 该任务拥有递归信号量的所有权。 任务成功获取几次递归互斥量, 就要返还几次,在此之前递归互斥量都处于无效状态, 其他任务无法获取, 只有持有递归信号量的任务才能获取与释放。

18.2 二值信号量的使用场景

在嵌入式操作系统中二值信号量是任务间、 任务与中断间同步的重要手段,信号量使用最多的一般都是二值信号量与互斥信号量。只有 0 和 1 两种情况的信号量称之为二值信号量。
用途:做标记。比全局变量好的地方在于:如果没有信号量任务会阻塞,当信号量释放的时候会让该任务就绪。
同步应用场景:
设我们有一个温湿度的传感器,假设是 1s 采集一次数据,那么我们让他在液晶屏中显示数据出来,这个周期也是要 1s 一次的,如果液晶屏刷新的周期是 100ms 更新一次,那么此时的温湿度的数据还没更新,液晶屏根
本无需刷新,只需要在 1s 后温湿度数据更新的时候刷新即可,否则 CPU 就是白白做了多次的无效数据更新, CPU 的资源就被刷新数据这个任务占用了大半,造成 CPU 资源浪费,如果液晶屏刷新的周期是 10s 更新一次,那么温湿度的数据都变化了 10 次,液晶屏才来更新数据,那拿这个产品有啥用,根本就是不准确的,所以,还是需要同步协调工作,在温湿度采集完毕之后,进行液晶屏数据的刷新,这样子,才是最准确的,并且不会浪费 CPU的资源。
中断同步的应用场景:
我们在串口接收中,我们不知道啥时候有数据发送过来,有一个任务是做接收这些数据处理,总不能在任务中每时每刻都在任务查询有没有数据到来,那样会浪费 CPU 资源,所以在这种情况下使用二值信号量是很好的办法,当没有数据到来的时候, 任务就进入阻塞态,不参与任务的调度,等到数据到来了,释放一个二值信号量, 任务就立即从阻塞态中解除,进入就绪态,然后运行的时候处理数据,这样子系统的资源就会很好的被利用起来。

18.3 二值信号量的运作机制

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

18.4 计数信号量运作机制

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