void Funnction(void)
{
OS_ENTER_CRITCAL();
.
OS_EXIT_CRITCAL();
}
互斥条件:
实现任务间通信最简单的办法是使用共享数据结构,特别是党所有的任务都在一个单一地址空间下,这种处理特别简便,如果能使用变脸个(如全局变量,指针,缓冲区,链表以及循环缓冲区等),使用共享数据结构通信就更为容易,虽然共享数据区法简化了任务间的信息交换,但是必须保证每个任务在处理共享数据时的排他性,以避免竞争和数据的破坏,与共享资源打交道时,使之满足互斥条件最一般的方法有:
关中断;
使用测试并置位指令
禁止做任务切换
利用信号量
关中断和开中断
void Funnction(void)
{
OS_ENTER_CRITCAL();
.
OS_EXIT_CRITCAL();
}
必须十分小心,关中断的时间不能太长,因为她影响这个系统的中断响应时间,即中断延迟时间,当改变或复制某个变量的值时,应考虑使用这种方法,这也是在终端服务子程序中处理共享变量或共享数据结构的唯一方法,在任何情况下关中断的时间都尽量短,使用某种实时内核,一般地说,关中断的时间最长不超过内核本身的关中断时间,这样就不会影响系统中断延迟
测试并置位
如果不适用实时内核,那么当2个任务共享一个资源是,须约定好,先测试一全局变量,如果该变量为0,允许该任务与共共享资源打交道,为防止另一任务也要使用该资源,第一个得到资源的任务只需简单的将全程变量置位1,这通常称作测试并置位操作,
01
02 Disable interrupts; //关中断
03 if('Access Variable' is 0) //如果资源未占用,标志为0
04 {
05 Set variable to 1; //置资源已占用,标志为1
06 Reenable interrupts; //重开中断
07 Access the resource; //处理该资源
08 Disable interrupts; //关中断
09 Set the 'Access Variable' back to 0;//置资源已占用,标志为1
10 Rennable interrupts; //重新开中断
11 }
12 else
13 {
14 Reenable interrupts; //开中断
15 /*资源不可使用,以后再试*/
16 }
禁止,然后允许任务切换
如果任务不与中断服务子程序共享变量或数据结构,可以使用“禁止,然后允许任务切换”2个或2个以上的人物画可以共享数据而不发生竞争,注意,此时虽然人任务切换时禁止了,但中断还是开着的,如果这时中断来了,中断服务子程序会在这一临界区内立即之心个,中断服务子程序结束时,尽管有优先级高的任务已经进入就绪态,内核还是返回到原来被中断了的任务,由于ISR返回到被中断了的任务,内核的行为与不可剥夺型内核十分相像(至少在任务切换被上锁期间如此),直到执行完给任务切换开锁函数OSSchedUnock(),内核再看是否有优先级别更高的任务被中断服务子程序激活而进入就绪态,如果有,则做任务切换,虽然这种方式是可行的,但应该尽量避免“禁止任务切换”等操作
01 void Funnction(void)
02 {
03 OSSchedLock();
04 .
05
06 .
07 /*在这里处理共享数据*/
08 .
09 .
10 OSSchedUnlock();
11 }
信号量:
信号量实际上是一种约定机制,信号量有用:
控制共享资源的使用权(满足互斥条件)
标志某事件的发生
使2个任务的行为同步
信号像一把钥匙,任务要运行下去,需先拿到这把钥匙,如果信号已被别的任务占用个,那么该任务只得被挂起,直到信号被当前使用者释放,换句话说,申请信号的任务是在说“把钥匙给我,如果岁正在用着,我只好等”信号是只有2个值的变量,信号量是计数式的,只取2个值的信号,是只有2个值0和1的量,因此也成为信号连个,计数式信号量的值可以是0~255,或0~65536,取决于信号量规约机制使用的是8位、16位还是32位,到底是几位,实际上取决于所用的内核的类型,根据信号量的值,内核跟踪那些等待信号量的任务
一般地说,对信号量只能实施3中操作,:初始化,也可称作为建立;等信号,也可称作为挂起;给信号或发信号,信号量初始化时,要给信号量赋初值,等待信号量的任务表应清为空
想得到信号量的任务,须执行等待操作,如果该信号量有效(信号量值大于0)。则信号量减1,任务得意继续运行,如果信号量的值为0,等待信号量的任务就被列入等待信号量任务表,多数内核允许定义等待超时,如果等待时间超过了某一设定值,该信号量还是无效,则等待信号量的任务进入就绪态,准备运行,并返回出错代码(指出发生了等待超时错误)
任务以发信号操作释放信号量,如果没有任务等待信号量,那么信号量的值仅仅是简单地加1;如果有任务等待该信号量,那么就会有一个任务进入就绪状态,信号量的值也就不加1,于是钥匙给了等待信号量的诸任务中的一个任务,至于给了哪个任务,要看内核是如何调度的。
受到信号量的任务可能是以下两者之一:
等待信号量任务中优先级最高的;
最早开始等待信号量的任务,即按先进先出FIFO的原则
有些内核具有选择项,允许在信号量初始化时,选定上述2种方法中的一种,但uco只支持优先级法
如果进入就绪态的任务比当前运行的任务优先级高,(假设当前任务释放的信号量子活了自己优先级高的任务),则内核做任务切换(假设使用的是可剥夺型内核),高优先级的任务开始运行,当前任务被挂起,直到又变成就绪态中优先级最高的任务
要与同一共享数据打交道的任务,调用等待信号量函数OSSemPend(),处理完共享数据后,要调用信号量函数PSSemPost(),要注意的是,在使用信号量之前,一定要对该信号量初始化,作为互斥条件,信号量做初始化,作为互斥条件,信号量初始化为1,使用信号量处理共享数据不增加中断延迟时间,如果中断服务程序或当前任务激活了一个高优先级的任务,则高优先级的任务立即开始执行
01 void Function(void)
02 {
03 INT8U err;
04 OSSemPend(SharedDataSem, 0, &err); //等待信号量,当得到信号量,将其置位0
05 .
06 ./*共享数据的处理再次进行(中断是开着的)*/
07 .
08 .
09 OSSemPost(SharedDataSem);//释放信号量
10 }
每个任务都直到有个新号表示资源可不可以使用,要想使用量用一把钥匙表示,想使用打印机,先要得到这把钥匙
然而有些情况下,最好把信号量藏起来,各任务在于某一资源打交道时,并不知道实际上市在申请得到一个信号量
01 INT8U CommSendCmd(char *cmd, char *response, INT16U timeout)
02 {
03 Acquire port semaphpre;
04 Send command to device;
05 wait for respose(with timeout);
06 if(time out)
07 {
08 Release semaphore;
09 return(error code);
10 }
11 else
12 {
13 Release semaphore;
14 return(no error);
15 }
16 }
计数式信号量由于某资源可同时为几个任务所用,例如,用信号量管理缓冲区阵列,缓冲区阵列中共有10个缓冲区,任务通过调用申请缓冲区函数BufReq()、向缓冲区管理方申请得到缓冲区使用权,当缓冲区使用权不在需要时,通过调用释放缓冲区函数BufRel(),将缓冲区还给管理方
01 Buf *BufReq(void)
02 {
03 BUF *ptr;
04 Acquire a semaphore;
05 Disable interrupts;
06 ptr = BufFreeList;
07 BufFreeList = ptr->BufNext;
08 ENABLE interrupts;
09 return(ptr);
10 }
11
12 void BufRel(Buf *ptr)
13 {
14 Diable interrupts;
15 ptr->BufNext = BufFreeList;
16 BufFreeList = ptr;
17 Enable interrupts;
18 Release semaphore;
19 }
缓冲区阵列管理方满足前10个申请缓冲区的任务,就好像有10把钥匙可以发送给诸任务。当所有的钥匙都用完后,申请缓冲区的任务被挂起,直到信号量重新变为有效,缓冲区管理程序在处理链表指针时,为满足互斥条件中断是关掉的,任务使用完某一缓冲区,通过缓冲区释放函数BufRel()将缓冲区还给系统,系统先将缓冲区指针掺入空闲缓冲区链表中,然后再给信号量+1,或释放该信号连个,这以过程隐含在缓冲区管理程序BufReq()和BufRel()之中,调用这2个函数的任务不用关函数内部的详细过程
信号量长被用过了头,处理简单的共享变量也使用信号量,则是多余,请求和释放信号量的过程是法非相当的时间的,有时这种额外的符合是不必要的,可能只须关中断、开中断来处理简单的共享变量
同步:
可以利用信号量使某任务与中断服务同步(或者与另一个任务同步)信号量表示某一事件的发生,用来实现同步机制的信号量初始化成0,信号量用于这种类型的同步,称作为单相同步,一个任务做IO操作,然后等待信号回应,当IO操作完成时,中断服务程序(或另外一个任务)发出信号,该任务得到信号后继续往下执行
如果内核支持计数式信号量,那么信号量的值表示尚未得到处理的事件数,请注意,可能会有一个以上的任务在等待同一事件的发生,那么这种情况下内核会根据以下原则之一发信号给相应的任务
发信号给等待事件发生的任务中优先级最高的任务
发信号给最先开始等待事件发生的任务
根据不同的应用,发信号以标志事件发生的中断服务或任务也可以是多个
2个任务可以用2个信号量同步他们的行为,这叫做双向同步,双向同步与单向同步类似,只是2个任务要相互同步,双向同步不可能在任务与ISR之间实施,因为ISR不可能等待一个信号量
01
02 void Task1()
03 {
04 for(;;)
05 {
06 Preform operation;
07 Signal tash #2;
08 Wati for signal form task #2;
09 Contiue operation;
10 }
11 }
12
13 void Task2()
14 {
15 for(;;)
16 {
17 Perform operation;
18 Signal task #1;
19 Wati for signal form task #1;
20 continue operation;
21 }
22 }