VxWorks中文网站 > 热门推荐 > VxWorks信号量怎么创建 VxWorks信号量优先级反转怎么排查
教程中心分类
VxWorks信号量怎么创建 VxWorks信号量优先级反转怎么排查
发布时间:2026/07/21 09:52:03

  VxWorks信号量应该如何创建,以及VxWorks信号量出现优先级反转时应该怎样排查,这两个问题在实时任务同步和共享资源保护中经常遇到。VxWorks提供了二进制信号量、计数信号量和互斥信号量,不同类型所要解决的问题并不相同。如果把任务通知、资源计数和临界区保护混合在一起,全都使用同一种信号量,程序在低负载时或许还能暂时运行,但等到负载变高或者任务优先级发生变化之后,就很容易出现长时间的阻塞。排查优先级反转的时候,需要重点关注哪一个任务正持有信号量、有哪些任务正在等待,以及持有者迟迟无法释放的原因。

  一、VxWorks信号量怎么创建

 

  在创建信号量以前,需要先明确它是准备用于任务之间的同步,还是用于资源数量的管理,又或者是用来保护某一块共享数据。VxWorks中不同的信号量,虽然都是通过semTake和semGive来完成获取与释放的,但是它们的创建方式、所有权规则以及适用的场景,却是各不相同的。

 

  1、根据使用场景选择类型

 

  在任务同步的场合,可以通过【semBCreate】来创建一个二进制信号量;如果要管理多个同类型的资源,则使用【semCCreate】;当需要保护共享设备、缓存区或者链表的时候,就应当优先使用【semMCreate】去创建一个互斥信号量。

 

  二进制信号量只有可用和不可用两种状态,比较适合用来做事件通知,有时也可以被当作简单的互斥来使用;计数信号量则适合用来表示缓冲区的数量、消息槽位或者资源池中还有多少余量。真正在进行临界区保护的时候,互斥信号量是更加合适的,因为它能够记录当前是哪一个任务占有着它,并且可以提供递归获取、删除安全以及优先级继承这些机制。

 

  2、配置等待队列和反转保护

 

  在对共享资源进行保护的时候,可以根据项目的实际要求,使用semMCreate(SEM_Q_PRIORITY|SEM_INVERSION_SAFE)这样的写法;如果还需要避免持有锁的那个任务被意外删除掉,还可以再增加一个SEM_DELETE_SAFE。

 

  SEM_Q_PRIORITY这个选项,是让正在等待的任务按照优先级的高低来排队;SEM_INVERSION_SAFE则是将优先级继承的功能打开,而且这两个选项需要配合在一起使用。当高优先级的任务被阻塞之后,正持有信号量的那个低优先级任务,会临时把等待队列当中比较高的那个优先级继承过来,直到它把自己持有的相关互斥信号量全部释放为止。

 

  3、正确执行获取和释放

 

  在进入临界区之前,需要调用【semTake】,在退出临界区之前,则要调用【semGive】,同时还要检查一下返回的状态。

 

  semTake可以使用WAIT_FOREVER,也就是一直等待,也可以使用NO_WAIT,即不等待直接返回,还可以给它指定一个具体的超时tick数。在开发和排查阶段,不一定每次都要使用无限等待,设置一个比较合理的超时时间,反而更容易让那种某个任务长时间占用资源的情况暴露出来。互斥信号量只能由当初获取它的那一个任务去释放,而且绝对不能在中断服务程序当中去获取或者释放;如果同一个任务递归地获取了很多次,那么就必须执行相同次数的semGive,才能真正地把它释放掉。

 

  二、VxWorks信号量优先级反转怎么排查

 

  典型的优先级反转是这样的:先有一个低优先级的任务占用了资源,接着高优先级的任务开始等待,而这时中等优先级的任务又不停地去抢占低优先级任务的执行时间。结果就是,高优先级的任务表面上是在等待低优先级的任务,实际上等待的时间却被中等优先级的任务不断地拉长。在进行排查的时候,不能只去看哪一个任务被阻塞住了,还需要把完整的占用过程和调度过程全都还原出来。

 

  1、确认信号量类型和创建参数

 

  首先要检查一下,用来保护共享资源的那一个,到底是互斥信号量,还是普通的二进制信号量。如果程序是用semBCreate去保护临界区的,那么它就没有互斥信号量所提供的那种所有权和优先级继承机制。然后再去检查semMCreate是不是同时设置了SEM_Q_PRIORITY和SEM_INVERSION_SAFE,要避免出现这种情况:从代码上看好像已经启用了反转保护,可实际上创建时填写的参数并没有生效。

  2、记录持有者和等待任务

 

  在调试的时候,可以通过【semInfo】或者是【semShow】这一类的手段,去查看信号量当前的状态、等待队列,还有与之相关的任务,然后再结合任务的优先级以及运行状态,来确定阻塞的链条。

 

  如果只是记录一个SEM_ID,很多时候是很难定位到具体是哪一块资源出了问题的,特别是项目中存在着大量生命周期很短的信号量的时候。可以为那些关键的同步对象建立起一个清晰的名称,或者是封装一个统一的创建接口,同时把获取的时间、释放的时间还有任务的标识都一起记录下来。这样一来,当延迟出现的时候,才能判断清楚,到底是某一个信号量长时间没有被释放,还是多个信号量之间形成了相互等待的局面。

 

  3、检查临界区是否执行过久

 

  低优先级的任务在持有锁的这段时间里面,如果还继续去进行文件访问、网络通信、设备等待、调用延时函数,或者做其他一些有可能把自己阻塞住的操作,那么就算已经把优先级继承打开了,高优先级的任务仍然需要等着整个临界区全部执行完毕。应该把针对共享数据的读取和更新保留在锁的保护范围以内,把那些耗时的计算以及对外部模块的调用,都移到锁的外面去,尽最大可能去缩短信号量被占用的时间。

 

  三、优先级继承生效后仍然卡顿怎么办

 

  把SEM_INVERSION_SAFE打开,只能缓解那种最典型的反转,它并不能解决死锁、忘记释放,或者是复杂的嵌套互斥这类问题。如果系统当中仍然出现了长时间等待的情况,那就需要接着去检查锁的获取顺序,还有任务在异常退出时的处理路径。

 

  1、检查嵌套信号量

 

  当一个任务同时持有多个互斥信号量的时候,它继承过来的那个比较高的优先级,有可能会一直保留到所有相关的互斥信号量全都被释放掉为止。如果多个任务采用不一样的顺序去获取A锁和B锁,还有可能形成循环等待。VxWorks关于互斥信号量的文档资料也提示过,嵌套的互斥会让反转关系变得更加复杂。

 

  在项目当中,应当把获取锁的顺序固定下来,比如说,让所有的任务都先去获取设备锁,然后再去获取数据锁,不能允许有的任务反过来获取。如果真的遇到了必须嵌套的情况,还需要把每一层semTake是否成功都记录下来,并且在异常返回的时候,按照与获取顺序相反的方向,把已经拿到的锁一层一层地释放掉。

 

  2、检查遗漏释放和任务异常

 

  要对所有提前return的地方、发生错误后跳转的地方,还有异常处理的分支,全部仔细检查一遍,确认每一次成功执行semTake之后,最终都能对应执行一次semGive。任务在持有互斥信号量的时候,如果异常停止了,系统并不会在不知道共享资源状态的情况下,自动安全地把那个信号量回收回来,这样一来,那块资源就有可能长期处于不可用的状态。

 

  3、通过时间数据确认问题

 

  可以在关键的semTake前后都打上时间戳,然后分别去统计等待花费的时间和实际持有花费的时间。如果等待的时间很长,可是持有时间却很短,那么可能需要去检查一下调度和嵌套方面的问题;如果持有时间本身就已经很长了,那就应当直接去对临界区进行优化。跟那种仅仅观察任务“卡住了多长时间”的做法比起来,把这两段时间分开来看,更容易判断出问题真正的位置在哪里。

  总结

 

  VxWorks信号量应该如何创建,以及VxWorks信号量优先级反转应该怎样排查,最关键的一点,是先把同步、计数和互斥这几种场景区分开来。在保护共享资源时,应当优先使用semMCreate,并且根据需要设置SEM_Q_PRIORITY、SEM_INVERSION_SAFE和SEM_DELETE_SAFE。一旦出现优先级反转,就要检查信号量的类型、创建时的参数、持有任务、等待队列以及临界区的耗时;如果启用了优先级继承之后情况依然不正常,那么还需要接着排查嵌套锁、遗漏释放和任务异常退出。信号量本身只不过是一种调度工具,真正决定实时性能的,还是临界区是否足够短、获取锁的顺序是否统一,以及错误路径能不能把资源完整地释放掉。

135 2431 0251