任务与任务之间进行通信最简便的方法是使用共享数据结构,特别是当所有的任务都在一个单一的地址空间。虽然,这种方法比较简单,但是要避免当一个任务在使用这个共享数据结构的时候另一个任务也使用它,这就要求每个任务在使用这个数据结构的时候都具有排它性,以避免竞争和数据被破坏。常用的使一个共享资源满足互斥的条件的方法有:
(1) 关中断。OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL();需要注意的时,关中断的时间要尽量的短,避免影响中断的响应从而导致系统的实时性大打折扣。
(2) 使用测试并置位命令。这种方法时通过约定(当该共享资源被使用时,某个标志就被置位,通过查询这个标志就可以知道这个共享资源当前是否被使用)实现的。这种操作被称为:TAS(Test And Set)操作。
(3) 禁止作任务切换。使别的任务得不到调度。不推荐这么做。具体的命令为:OSSchedLOck()和OSSchedUnlock();
(4) 利用信号量。信号量可以用来标志某个事件的发生,使得2个任务的行为同步。对信号量只能实施3种操作:初始化、给信号和发信号量。特别要注意的是收到信号量的任务可能是优先级最高的,也有可能是最早开始等待信号量的任务,至于具体是哪种任务该得到信号量,由内核决定。uC/OS中只支持优先级最高的任务。
死锁(deadlock),顾名思义就是2个任务陷入无限的等待对方控制的资源。如:任务1正在独享R1资源,任务2正在独享R2资源,但是此时任务1又要独享R2资源,任务2又要独享R1资源,那么就导致了这两个任务都无法继续执行。
单向同步:包括了ISR发信号量给任务的同步和任务发信号量给任务的同步两种。
双向同步:只能是任务与任务之间的互相同步。ISR不可能等待一个信号量。
事件标志包括“或”型同步和“与”型同步。
“或”型同步:只要事件标志组中有一个事件发生就执行。
“与”型同步:需要事件标志组中所有事件都发生才执行。
任务之间需要进行数据交换有2种途径:全局变量和发送消息。
消息邮箱里面放着消息的指针,接收消息的任务只需根据得到的指针就可以得到需要的数据了。消息邮箱也可以用在信号量来使用(用消息邮箱是否空来标志资源是否被占用)。
消息队列实际上是邮箱阵列。
非屏蔽中断(NMI):关不掉的中断,用于对事件要求非常苛刻的中断服务中,如:系统掉电重要资源保护。非屏蔽中断传递参数必须使用全局变量。
特定的周期性中断,好比是整个系统的心脏的脉动。中断的间隔取决于不同的应用,一般取10ms~200ms,时钟节拍越短,系统额外的消耗就越大。还需要注意的是,系统提供等待整数倍的时钟节拍,这个定时是不准确的。
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL();开中断与关中断,这两个宏调用位于OS_CPU.H中,这两个宏应当是成对使用的,有关就有开。特别需要注意的是,在调用一些OSTimeDel()之类的功能函数之前不能关中断,否则应用程序会崩溃,这主要是因为中断被关掉了,时钟节拍中断就一直得不到响应,挂起的任务也就不能执行。通常在调用uC/OS的功能函数的时候中断应当是开着的。
在uC/OS中OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()可以用不同的方法实现,这取决于用户打算移植到的处理器的性能以及所使用的C编译器。
OS_CIRTICAL_METHOD == 1 :
这种方法是最简单实现开关中断。直接使用的是处理器的开关中断的指令。
#if OS_CRITICAL_METHOD == 1
#define OS_ENTER_CRITICAL() asm CLI /* Disable interrupts */
#define OS_EXIT_CRITICAL() asm STI /* Enable interrupts */
#endif
优点:
缺点:
OS_CRITICAL_METHOD == 2 :
这种方法是在堆栈中保存中断的开关状态,然后再关中断。在OS_EXIT_CRITICAL()的时候只需要从堆栈中弹出原来中断的开关状态就可以了。
#if OS_CRITICAL_METHOD == 2
#define OS_ENTER_CRITICAL() asm {PUSHF; CLI} /* Disable interrupts */
#define OS_EXIT_CRITICAL() asm POPF /* Enable interrupts */
#endif
优点:
缺点:
OS_CIRTICAL_METHOD == 3 :
这个方法是利用了一些编译器提供了扩展功能,用户可以得到当前处理器状态字的值,并保存在C函数的局部变量中,那么就可以利用这个变量来恢复PSW。
#if OS_CRITICAL_METHOD == 3
#define OS_ENTER_CRITICAL() (cpu_sr = OSCPUSaveSR()) /* Disable interrupts */
#define OS_EXIT_CRITICAL() (OSCPURestoreSR(cpu_sr)) /* Enable interrupts */
#endif
任务通常是一个无限循环。uC/OS-II可以管理的任务为64个,建议使用的只有56个,其他的8个任务是这样子的:优先级为0、1、2、3、OS_LOWEST_PRIO、OS_LOWEST_PRIO-1、OS_LOWEST_PRIO-2、OS_LOWEST_PRIO-3。这些优先级的任务有被uC/OS-II已经占用的也有将来可能会用到的。
#define OS_LOWEST_PRIO 63 /* Defines the lowest priority that can be assigned */
/* ... MUST NEVER be higher than 63! */
(1) 睡眠态:
任务驻留在ROM或RAM中但是还没有交给uC/OS-II来管理。
(2) 就绪态:
任务一旦建立就立即进入就绪态,准备运行。任务的建立可以是多任务运行之前,也可以是多任务运行中。任务中也可以建立另一个任务,如果被建立的任务的优先级高于建立它的任务,它立刻进入运行态。
(3) 运行态:
多任务建立后,可以调用OSStart()开始运行多任务,该函数只能在启动时调用一次。CPU只有一个,任何时刻只有一个任务处于运行态(掌握CPU的使用权)。uC/OS-II是基于优先级调度的,所以要一个任务处于运行态,那么就需要所有优先级高于该任务的任务处于等待状态或者这些高优先级任务被删除了。
(4) 等待态:
一种情况是为了避免高优先级的任务称霸CPU,那么就需要周期性的把这个任务挂起让其他较低的优先级的任务有机会被执行。可以通过调用OSTimeDly()和OSTimeDlyHMSM()来将任务自身延迟一段时间。
另外一种情况是当运行着的任务需要等待某一个事件的发生的时候,那么此时它占着CPU什么也不干太浪费,趁着等待的时刻让其他低优先级的任务运行运行就非常不错了。那么这个高优先级的任务可以根据自己具体的需要调用以下函数是自己处于等待状态:OSFlagPend()、OSSemPend()、OSMutexPend()、OSMboxPend()或OSQPend()。总结一下,都是一些Pend函数。
(5) 中断服务态:
正在运行的任务被中断打断的状态。这里需要注意的是,当中断返回后不一定就返回到被打断的任务,因为在uC/OS-II中始终都是以任务的优先级来判定哪个任务该执行。
文章评论(0条评论)
登录后参与讨论