这一章节主要是基于raw os提供的api函数去管理共享资源。一个共享的资源同通常是一个全局变量,比如静态的或者非静态的全局变量,全局数组,全局结构体,或者是硬件设备等。 当保护一个共享资源的时候,可以采用的方法有semaphore(信号量),mutex(互斥量),关系统抢占,关中断等方法。 任务之间共享全局变量,全局数组,全局结构体,或者是硬件设备等是一个很普遍的做法,也能大大简化系统设计。虽然任务之间的共享全局数据简化了系统设计,但是有一点很重要的是需要保护这些共享资源不被冲突,也就是说涉及到保护临界区的问题。 下面谈谈semaphore(信号量),mutex(互斥量),关系统抢占,关中断等方法具体会在什么场合下使用。 file:///C:/Users/ASUS/AppData/Local/Temp/ksohtml/wps_clip_image-5992.png 资源共享处理方法 file:///C:/Users/ASUS/AppData/Local/Temp/ksohtml/wps_clip_image-6473.png 开关中断的方法 当中断和任务直接在访问共享资源上冲突的候,这是唯一的处理方法。另外处理一些速度非常快的临界区资源的时候可以采用此方法。当任务和任务之间产生资源冲突的时候,绝大部分情况下不推荐使用此种方法,因为会给系统带来中断延迟。 开关系统抢占 当一个任务访问共享资源的时候关了抢占的话,其它任何任务都抢占不了此任务。此方法会带来任务间的延迟,即处于高优先级的任务只能等待低优先级的任务开了系统抢占后才能运行。但是这个方法比关中断的方法要好,因为不会带来中断延迟。 信号量(semaphore) 当所有处理共享资源的任务没有规定的最后期限的时候 ,也就是说任务访问共享资源的时候,时间并不紧急的情况下,可以使用信号量。信号量api的执行时间是比较快的相比mutex(互斥量)。 mutex(互斥量) 互斥量是推荐的形式来保护共享资源,因为互斥量处理了优先级反转的问题。对于处理共享资源的任务时间上有需求的时候,推荐用互斥量。 对于共享资源的保护需要注意的是,在任何时候避免使用开关系统抢占,虽然这 个接口是放出来了,万不得已一定不要使用,因为会增大系统任务的最大延迟时 间。对于任务之间的互斥,尽可能使用mutex,不要使用信号量,信号量更多的 是使用在任务之间同步的场合。对于开关中断的方法尤其要谨慎,要保证开关中 断间的代码要足够短,运行时间要足够短,因为一旦关了中断,系统的实时性每 一个us都会下降。 死锁 死锁是一种情况,两个任务相互等待访问共享的的资源,两个任务相互等待被对 方占有的锁。 具体举例分析如下 void t1(void *p_arg) { while (1) { 等待事件发生; 得到锁M1 (1) 处理共享资源R1 …………………. ………………… 中断发生 (2) 试图得到锁M2 处理共享资源R2 } void t2(void *p_arg) { while (1) { 等待事件发生; 得到锁M2 (3) 处理共享资源R2 …………………. ………………… ………………… 试图得到锁M1 (4) 处理共享资源R1 } 假设任务t1是低优先级任务,任务t2是高优先级任务。t2由于等待某一事件睡眠中,t1执行到(1)处代码的时候,得到了锁M1,执行到(2)处代码时发生了中断,中断里面唤醒了高优先级任务t2, 任务t2执行到(3)处代码时得到了锁M2,执行到(4)处代码时会试图去获得锁M1,因为M1已经被t1获得,所以此时任务t2只能等待锁M1的释放,然后切换给任务他t1,当t1执行到(2)处代码时,由于锁M2已经被任务t2获取,所以任务t1只能等待任务t2去释放锁M2。这样就形成了两个任务相互等待被对方占有的锁,两个任务将永远等待下去。 这个问题的解决方式可以有以下两种: 1 获得所有的锁再访问共享资源,注意获得锁的次序要统一。 void t1(void *p_arg) { while (1) { 等待事件发生; 得到锁M1 得到锁M2 处理共享资源R1 处理共享资源R2 } void t2(void *p_arg) { while (1) { 等待事件发生; 得到锁M1 得到锁M2 处理共享资源R1 处理共享资源R2 } 2 各任务获得锁的次序需要统一。 void t1(void *p_arg) { while (1) { 等待事件发生; 得到锁M1 处理共享资源R1 得到锁M2 处理共享资源R2 } void t2(void *p_arg) { while (1) { 等待事件发生; 得到锁M1 处理共享资源R1 得到锁M2 处理共享资源R2 }
|