信号量
skaiuijing
简单谈谈RTOS机制的设计区别吧。
笔者以前学过几种RTOS,应该还是可以勉强答一下的。只说说大概印象吧,毕竟也不是天天使用RTOS做项目的单片机开发工程师,答错了勿怪。
说实话,其实从操作系统的角度来看的话,并没有多少区别,但是从机制的设计角度来看,区别还是很大的。就以国内最流行的两个RTOS:FreeRTOS和RT-Thread举例吧。
FreeRTOS是使用面向过程编写的,代码往往是一层套一层。就笔者个人体验来说吧,可读性很差。笔者当时研究FreeRTOS源码时,往往是直接调试查看感兴趣的内存变量,一行行读FreeRTOS的源码有点受罪。
RT-Thread是使用面向对象编写的,还是比较模块化的,代码可读性也高。因为它借鉴了不少linux内核的风格,笔者个人是比较喜欢这种风格的。
笔者有段时间经常喜欢阅读linux内核的源码,不得不说,linux内核源码的风格真的是优雅又简明。
FreeRTOS的特点是稳定可靠,RT-Thread的特点是linux化。
从线程机制和IPC机制谈谈吧。
线程机制个人感觉区别不大,从功能来看无非就是内存管理和任务调度那一套,创建任务、内存分配、优先级、临界区、延时阻塞啥的。笔者之前尝试过写一个小RTOS,只用四百行代码也能完成这些功能,还在知乎上更过二十篇教程。
但是从设计来看,面向对象和面向过程的思想体现的程序还是区别挺大的,比如RT-Thread就有对象容器这个东西,也使用一些继承机制。
至于IPC,这确实可以详细谈一谈,笔者个人是觉得挺有意思的。就拿三个经典的IPC机制举例:消息队列、信号量、互斥锁。
大家都知道信号量是有P操作和V操作,然后根据value初始化时的值不同,有两种不同的功能:同步和互斥。
二值信号量且初始化value设置为0,才可用于同步。
FreeRTOS的这三个机制都是在消息队列的基础上实现的,消息队列初始化时,value就是写入消息的数量,相关的计数是0,这也就注定了它的P操作和V操作只能用于同步:V操作是发消息,P操作是获取消息。那么怎么实现信号量呢?
FreeRTOS的设计机制有点意思:信号量反正只要计数值,不发消息不就完事了,只计数。所以FreeRTOS的信号量在不启用互斥量功能时,就默认是同步功能,这是在消息队列的基础上直接扩展的结果。
那么问题来了,既然初始值是0,那就是同步功能,那么怎么做到互斥呢?FreeRTOS的解决办法是,初始化时使用一次V操作,也就是发送一次消息,那么初始化的值就是1了,这样就能完美无缺的使用互斥量了。
这样就能只用一个消息队列,完成三种功能。
(笔者印象中是这样的,说错了勿喷,笔者也记不清FreeRTOS的具体源码了,只记得大概是这个过程)
说实话,这些操作配合FreeRTOS令人不适的源码,只能说代码复用率确实高,但笔者真是欣赏不来,对于笔者这种喜欢接口和模块化开发的来说,不怎么喜欢。
RT-Thread就没有进行重复利用了,而是分别编写,这中间最能体现区别的就是信号量了,RT-Thread的信号量初始化的值是设置的value,也就是说,它的信号量既可以用来互斥,又可以同步。
linux内核中绝大部分的信号量都是被用来互斥的,因为优先级反转的问题,还要有优先级继承机制。
用来同步的信号量几乎没有,因为信号量往往要在任务完成时进行通信,那么调用P操作的线程几乎总是要等待。消息的同步一般是由completion机制完成的。
一个信号量如果被用于信号同步,那么最好只能有一个发送者和一个接收者,不然会导致竞争的出现。这也是linux内核为什么使用completion机制的原因。
规范代码,计数信号量两种用途—–计数和资源互斥:
1.value初始化为0,计数功能。对谁获取谁释放没有要求。本质上是同步功能。
2.value初始化为资源数,互斥功能。必须要有持有者!
FreeRTOS的计数信号量和RT-Thread的计数信号量的功能是一样的,因为FreeRTOS的源码直接设置uxmessagewaiting为信号量个数。所以默认都是互斥。