ucos-II中的一些基本概念②

来源:互联网 发布:虚拟机ubuntu如何分区 编辑:程序博客网 时间:2024/05/29 08:01

一、互斥条件
满足互斥条件的一般方法有:
关中断
使用测试并置位指令
禁止做任务切换
利用信号量

1.开中断和关中断
这是在处理共享数据时保证互斥是最简便快捷的方法
上μC/OS-Ⅱ提供两个宏调用,允许用户在应用程序的C代码中关中断然后再开中断:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL(),使用示例如下:
程序清单2.4利用μC/OS_Ⅱ 宏调用关中断和开中断
void Function (void)
{
    OS_ENTER_CRITICAL();
    .
    .    /*在这里处理共享数据*/
    .
    OS_EXIT_CRITICAL();
}

需要注意的是,关中断的时间会影响整个系统的中断响应时间,所以关中断的时间不能太长,即要尽量短。这是在中断服务子程序中处理共享变量或共享数据结构的唯一方法。
如果使用某种实时内核,一般地说,关中断的最长时间不超过内核本身的关中断时间,就不会影响系统中断延迟。当然得知道内核里中断关了多久

2.测试并置位
如果不使用实时内核,当两个任务共享一个资源时,一定要约定好,先测试某一全程变量,如果该变量是0,允许该任务与共享资源打交道。为防止另一任务也要使用该资源,前者只要简单地将全程变量置为1,这通常称作测试并置位(Test-And-Set),或称作TAS。TAS操作可能是微处理器的单独一条不会被中断的指令,或者是在程序中关中断做TAS操作再开中断

3.禁止,再允许任务切换
如果任务不与中断服务子程序共享变量或数据结构,可以使用禁止、然后允许任务切换。可以实现两个或两个以上的任务可以共享数据而不发生竞争。但在实际操作中应当尽量避免禁止任务切换之类操作,因为内核最主要的功能就是做任务的调度与协调。禁止任务切换显然与内核的初衷相违。应该用给任务切换上锁,然后开锁的方法实现数据共享。

4.信号量
信号量实际上是一种约定机制,在多任务内核中普遍使用.信号量用于:
控制共享资源的使用权(满足互斥条件)
标志某事件的发生
使两个任务的行为同步
信号与信号量实际上是两个不同的概念
信号是只有两个值的变量,只有0和1两个取值
信号量是计数式的,计数式信号量的值可以是0到255或0到65535,或0到4294967295,取决于信号量规约机制使用的是8位、16位还是32位。到底是几位,实际上是取决于用的哪种内核。根据信号量的值,内核跟踪那些等待信号量的任务。

对信号量只能实施三种操作:
初始化(INITIALIZE),也可称作建立(CREATE);
等信号(WAIT)也可称作挂起(PEND);
给信号(SIGNAL)或发信号(POST)。
信号量初始化时要给信号量赋初值,等待信号量的任务表(Waiting list)应清为空。

想要得到信号量的任务执行等待(WAIT)操作。如果该信号量有效(即信号量值大于0),则信号量值减1,任务得以继续运行。如果信号量的值为0,等待信号量的任务就被列入等待信号量任务表。

务以发信号操作(SIGNAL)释放信号量。如果没有任务在等待信号量,信号量的值加1。如果有任务在等待该信号量,那么就会有一个任务进入就绪态,信号量的值也就不加1。于是钥匙给了等待信号量的诸任务中的一个任务。收到信号量的任务可能是以下两者之一
等待信号量任务中优先级最高的,或者是
最早开始等待信号量的那个任务,即按先进先出的原则(First In First Out ,FIFO)
μC/OS-Ⅱ只支持优先级法。如果进入就绪态的任务比当前运行的任务优先级高(假设,是当前任务释放的信号量激活了比自己优先级高的任务)。则内核做任务切换(假设,使用的是可剥夺型内核),高优先级的任务开始运行。当前任务被挂起。直到又变成就绪态中优先级最高任务。

下面的代码是使用信号量处理数据的示例
程序清单2.7   通过获得信号量处理共享数据
OS_EVENT *SharedDataSem;
void Function (void)
{
    INT8U err;
    OSSemPend(SharedDataSem, 0, &err);
    .
    .    /* You can access shared data in here (interrupts are recognized) */
    .    /*共享数据的处理在此进行,(中断是开着的)*/
    OSSemPost(SharedDataSem);
}
要注意的是,在使用信号量之前,一定要对该信号量做初始化。作为互斥条件,信号量初始化为1。使用信号量处理共享数据不增加中断延迟时间,如果中断服务程序或当前任务激活了一个高优先级的任务,高优先级的任务立即开始执行。
计数式信号量用于某资源可以同时为几个任务所用,比如用信号量管理缓冲区
信号量常被用过了头。处理简单的共享变量也使用信号量则是多余的。请求和释放信号量的过程是要花相当的时间的。有时这种额外的负荷是不必要的。用户可能只需要关中断、开中断来处理简单共享变量,以提高效率。

二、死锁
死锁也称作抱死,指两个任务无限期地互相等待对方控制着的资源。
最简单的防止发生死锁的方法是让每个任务都:
1.先得到全部需要的资源再做下一步的工作
2.用同样的顺序去申请多个资源
3.释放资源时使用相反的顺序

还可以使用定义等待超时的方法的方法来化解死锁。当等待时间超过了某一确定值,信号量还是无效状态,就会返回某种形式的出现超时错误的代码,这个出错代码告知该任务,不是得到了资源使用权,而是系统错误。

三、同步
可以利用信号量使某任务与中断服务同步(或者是与另一个任务同步,这两个任务间没有数据交换)
用来实现同步机制的信号量初始化成0,信号量用于这种类型同步的称作单向同步(unilateral rendezvous)。一个任务做I/O操作,然后等信号回应。当I/O操作完成,中断服务程序(或另外一个任务)发出信号,该任务得到信号后继续往下执行。

根据不同的应用,发信号以标识事件发生的中断服务或任务也可以是多个。
两个任务可以用两个信号量同步它们的行为。这叫做双向同步(bilateral rendezvous)。双向同步同单向同步类似,只是两个任务要相互同步。
在任务与中断服务之间不能使用双向同步,因为在中断服务中不可能等一个信号量。

四、事件标志
当某任务要与多个事件同步时,要使用事件标志。
若任务需要与任何事件之一发生同步,可称为独立型同步(即逻辑或关系)。任务也可以与若干事件都发生了同步,称之为关联型(逻辑与关系)。
μC/OS-Ⅱ目前不支持事件标志.

五、任务间的通讯
有时很需要任务间的或中断服务与任务间的通讯。这种信息传递称为任务间的通讯。
任务间信息的传递有两个途径:通过全程变量或发消息给另一个任务。

用全程变量时,必须保证每个任务或中断服务程序独享该变量。
中断服务中保证独享的唯一办法是关中断。
如果两个任务共享某变量,各任务实现独享该变量的办法可以是关中断再开中断,或使用信号量(如前面提到的那样)。
任务只能通过全程变量与中断服务程序通讯,而任务并不知道什么时候全程变量被中断服务程序修改了,除非中断程序以信号量方式向任务发信号或者是该任务以查询方式不断周期性地查询变量的值。

六、消息邮箱
通过内核服务可以给任务发送消息。典型的消息邮箱也称作交换消息,是用一个指针型变量,通过内核服务,一个任务或一个中断服务程序可以把一则消息(即一个指针)放到邮箱里去。
一个或多个任务可以通过内核服务接收这则消息。
发送消息的任务和接收消息的任务约定,该指针指向的内容就是那则消息。
个邮箱有相应的正在等待消息的任务列表,要得到消息的任务会因为邮箱是空的而被挂起,且被记录到等待消息的任务表中,直到收到消息。

内核一般提供以下邮箱服务:

邮箱内消息的内容初始化,邮箱里最初可以有,也可以没有消息
将消息放入邮箱(POST)
等待有消息进入邮箱(PEND)
如果邮箱内有消息,就接受这则消息。如果邮箱里没有消息,则任务并不被挂起(ACCEPT),用返回代码表示调用结果,是收到了消息还是没有收到消息。

七、消息队列
消息队列用于给任务发消息。消息队列实际上是邮箱阵列。
通过内核提供的服务,任务或中断服务子程序可以将一条消息(该消息的指针)放入消息队列。同样,一个或多个任务可以通过内核服务从消息队列中得到消息。
发送和接收消息的任务约定,传递的消息实际上是传递的指针指向的内容。
通常,先进入消息队列的消息先传给任务,也就是说,任务先得到的是最先进入消息队列的消息,即先进先出原则(FIFO)。
然而μC/OS-Ⅱ也允许使用后进先出方式(LIFO)。

典型地,内核提供的消息队列服务如下:

消息队列初始化。队列初始化时总是清为空。
放一则消息到队列中去(Post)
等待一则消息的到来(Pend)
如果队列中有消息则任务可以得到消息,但如果此时队列为空,内核并不将该任务挂起(Accept)。如果有消息,则消息从队列中取走。没有消息则用特别的返回代码通知调用者,队列中没有消息。

八、中断
中断是一种硬件机制,用于通知CPU有个异步事件发生了。中断一旦被识别,CPU保存部分(或全部)现场(Context)即部分或全部寄存器的值,跳转到专门的子程序,称为中断服务子程序(ISR)。中断服务子程序做事件处理,处理完成后,程序回到:

在前后台系统中,程序回到后台程序
对不可剥夺型内核而言,程序回到被中断了的任务
对可剥夺型内核而言,让进入就绪态的优先级最高的任务开始运行
中断使得CPU可以在事件发生时才予以处理,而不必让微处理器连续不断地查询(Polling)是否有事件发生
通过两条特殊指令:关中断(Disable interrupt)和开中断(Enable interrupt)可以让微处理器不响应或响应中断。
关中断时间太长可能会引起中断丢失。微处理器一般允许中断嵌套,也就是说在中断服务期间,微处理器可以识别另一个更重要的中断,并服务于那个更重要的中断

九、中断延迟
可能实时内核最重要的指标就是中断关了多长时间。
所有实时系统在进入临界区代码段之前都要关中断,执行完临界代码之后再开中断。
中断延迟 = 关中断的最长时间 + 开始执行中断服务子程序的第一条指令的时间

十、中断响应
中断响应定义为从中断发生到开始执行用户的中断服务子程序代码来处理这个中断的时间。
典型地,执行用户代码之前要保护现场,将CPU的各寄存器推入堆栈。这段时间将被记作中断响应时间。
前后台系统,保存寄存器以后立即执行用户代码:中断响应时间 = 中断延迟 + 保存CPU内部寄存器的时间
不可剥夺内核,保存内部寄存器以后,用户的中断服务子程序代码全立即得到执行:中断响应时间 = 中断延迟 + 保存CPU内部寄存器的时间
可剥夺型内核,要先调用一个特定的函数,该函数通知内核即将进行中断服务,使得内核可以跟踪中断的嵌套:中断响应 = 中断延迟 + 保存CPU内部寄存器的时间 + 内核的进入中断服务函数的执行时间

十一、中段恢复时间
中断恢复时间定义为微处理器返回到被中断了的程序代码所需要的时间。
前后台系统:中断恢复时间 = 恢复CPU内部寄存器值的时间 + 执行中断返回指令的时间
不可剥夺内核:中断恢复时间 = 恢复CPU内部寄存器值的时间 + 执行中断返回指令的时间
可剥夺型内核:中断恢复时间 = 判定是否有优先级更高的任务进入了就绪态的时间 + 恢复那个优先级更高任务的CPU内部寄存器的时间 + 执行中断返回指令的时间






0 0
原创粉丝点击