由通信提出的问题
在裸机编程的过程中,我们经常会遇到函数需要另一些函数的数据信息,也就是通信,这时我们会怎么做呢?进行裸机开发的同学肯定都会说:使用全局变量,通过指针实现之类。使用全局变量快捷且高效。
但是在RTOS系统中,这会遇到一些问题:怎样防止许多线程同时进行对这个变量的访问?怎样观测通信是否已经发生,从而进行通信之后的工作?(例如:进行优先级转换,或者进行数据的处理)如果有个线程比较磨蹭,通信迟迟不发生,CPU岂不是要尴尬地等待好久。。
对于这些问题,操作系统给出了自己的通信方法与函数供使用者调用,当然,也最好使用操作系统的通信机制。
我们打开RTT 的IDE,然后打开内核的Setting,会看到RTT的一些设定:
可以看到,线程间通信一栏,这就是RTT给开发者提供的通信机制,在实际开发中,这些机制可以满足绝大部分需求。RTT系统默认打开了信号量,互斥量,事件集,邮箱,消息队列五种机制。
信号量:
二值信号量:
在我们的裸机开发过程中,经常会使用一个变量:Flag,大多数时候,这个叫做Flag的变量会在0和1之间反复横跳,用于标记某一件事情是否发生,并经常被用于中断。这个Flag就是一个信号量,实际上,它是我们最常使用的通信方式之一。
在RTT系统中,信号量会被获取或者释放(由获取/释放函数实现),当它被获取时,就为0,当它被释放,就为1。这样的信号量被称为二值信号量。
举一个例子(伪代码):
static void xxx1() { while(1) { /*一个传感器采集一个特别复杂的数据,需要3S,不需要CPU参与*/ xxx } } static void xxx2() { while(1) { /*将这个数据交给CPU进行分析,数据无效就舍弃*/ xxx
rt_thread_mdelay(xx) } }
在这个例子中,有两个线程,线程2中,CPU一直在等待数据,但数据却总是无效的(因为传感器还没采集到数据),这让CPU一直在进行数据验证,虽然CPU中间进行了一些延时,但仍然在无效数据上浪费了时间,这时,就需要信号量出马了
改良后数据如下(伪代码):
rt_sem_create(xxx); //信号量创建函数,参数中默认为1,表示是释放的,具体参数不赘述 static void xxx1() { while(1) { rt_sem_take(xxx) //获取信号量,这时信号量被置0 /*一个传感器采集一个特别复杂的数据,需要3S,不需要CPU参与*/ xxx rt_sem_release(xxx) //释放信号量,这时信号量被置1 } } static void xxx2() { while(1) { rt_sem_take(xxx) //获取信号量,当信号量已经被其他线程获取,就进入阻塞态等待,等待时间自定,这里不赘述 /*将这个数据交给CPU进行分析,数据无效就舍弃*/ xxx rt_sem_release(xxx) //释放信号量 rt_thread_mdelay(xx) } }
这里我们看到,当传感器开始工作时,信号量已经被获取了,这时进入线程二想要处理数据,就会进入阻塞态,让出CPU去做其他的事情,当传感器获得数据后,就会释放信号量,这时,线程二就从阻塞态中退出,从而去处理有效的数据。(注意:获取信号量进行相关操作后应立刻释放信号量,如果不进行释放,可能会导致其他线程无法运行)
经过这一番操作,无效数据不会被一次又一次的处理了,而是在传感器获得数据后,CPU直接去处理有效的数据,节省了许多无效操作。
计数型信号量:
计数型信号量与二值信号量类似,只不过范围从二值的[0,1],变为了[0,65535],这样它就可以被多个线程获取,获取的最大数目由使用者自定,使用的系统函数与二值信号量相同。
计数信号量经常被用于控制一些公共资源的访问数量,例如一个资源最多可以被x个线程同时访问,多于x时效率大大下降或者会出现错误(有点像网站服务器限制访客数量)。
互斥量:
互斥量是一种特殊的二值信号量,其与二值信号量的最大区别为引入了优先级继承机制。
优先级继承机制:如果一个高优先级的线程去申请一个已经被低优先级获得的互斥量,高优先级会进入到阻塞态,而持有这个互斥量的线程地位会瞬间与高优先级一样,取得高优先级相同的优先等级,这就是“优先级继承”
这样有什么好处呢?这里依然是一个栗子。
static void HIGH() { xxx } static void MIDDLE() { xxx } static void LOW() { xxx }
假设有三个线程,优先度分别为高,中,低。理所当然,开发者希望高优先级的越快处理越好,低优先级的则不是很紧要。
这时,如果有个二值信号量,被低优先级(LOW)获取。然后高优先级(HIGH)的也想要获取这个信号量,于是进入了阻塞等待。
但是当LOW准备用CPU运行时,MIDDLE却发话了:我有更紧要的任务,你让开。LOW小弟就被挤到了一边,毕竟它是优先级最低的。
这就产生了一个问题,开发者最希望运行的HIGH在等待信号量,而MIDDLE却在运行(假设MIDDLE和HIGH无关联,此时HIGH就要等待MIDDLE运行+LOW运行,而不是只等待LOW运行),LOW在等待CPU让出。这显然违背了开发者“高优先级越快越好”的想法。
怎样解决这个问题,这时就引入互斥量来解决这个问题,当HIGH进入阻塞态之后,LOW瞬间“继承”了HIGH的优先级,LOW小弟也暂时体验了当大哥的感觉,并把MIDDLE挤到一边,快速的处理起了数据,并在处理完后变回原来的优先级,将互斥量释放,HIGH得以继续运行。这样,通过暂时提高LOW的优先级,开发者所希望的HIGH处理越快越好的想法得以实现。
(暂时记录信号量和互斥量的学习笔记,文中可能有些理解和想法有略微的偏差,请注意=))