第7章 信号量管理.docx

上传人:b****5 文档编号:6238985 上传时间:2023-01-04 格式:DOCX 页数:12 大小:37.97KB
下载 相关 举报
第7章 信号量管理.docx_第1页
第1页 / 共12页
第7章 信号量管理.docx_第2页
第2页 / 共12页
第7章 信号量管理.docx_第3页
第3页 / 共12页
第7章 信号量管理.docx_第4页
第4页 / 共12页
第7章 信号量管理.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

第7章 信号量管理.docx

《第7章 信号量管理.docx》由会员分享,可在线阅读,更多相关《第7章 信号量管理.docx(12页珍藏版)》请在冰豆网上搜索。

第7章 信号量管理.docx

第7章信号量管理

第7章信号量管理

µC/OS-II中的信号量由两部分组成:

一个是信号量的计数值,它是一个16位的无符号整数(0到65,535之间);另一个是由等待该信号量的任务组成的等待任务表。

用户要在OS_CFG.H中将OS_SEM_EN开关量常数置成1,这样µC/OS-II才能支持信号量。

在使用一个信号量之前,首先要建立该信号量,也即调用OSSemCreate()函数(见下一节),对信号量的初始计数值赋值。

该初始值为0到65,535之间的一个数。

如果信号量是用来表示一个或者多个事件的发生,那么该信号量的初始值应设为0。

如果信号量是用于对共享资源的访问,那么该信号量的初始值应设为1(例如,把它当作二值信号量使用)。

最后,如果该信号量是用来表示允许任务访问n个相同的资源,那么该初始值显然应该是n,并把该信号量作为一个可计数的信号量使用。

µC/OS-II提供了5个对信号量进行操作的函数。

它们是:

OSSemCreate(),OSSemPend(),OSSemPost(),OSSemAccept()和OSSemQuery()函数。

图F7.5说明了任务、中断服务子程序和信号量之间的关系。

图中用钥匙或者旗帜的符号来表示信号量:

如果信号量用于对共享资源的访问,那么信号量就用钥匙符号。

符号旁边的数字N代表可用资源数。

对于二值信号量,该值就是1;如果信号量用于表示某事件的发生,那么就用旗帜符号。

这时的数字N代表事件已经发生的次数。

从图F7.5中可以看出OSSemPost()函数可以由任务或者中断服务子程序调用,而OSSemPend()和OSSemQuery()函数只能有任务程序调用。

图F7.5任务、中断服务子程序和信号量之间的关系——Figure7.5

7.00建立一个信号量,OSSemCreate()

程序清单L7.9是OSSemCreate()函数的源代码。

首先,它从空闲任务控制块链表中得到一个事件控制块[L7.9

(1)],并对空闲事件控制链表的指针进行适当的调整,使它指向下一个空闲的事件控制块[L7.9

(2)]。

如果这时有任务控制块可用[L7.9(3)],就将该任务控制块的事件类型设置成信号量OS_EVENT_TYPE_SEM[L7.9(4)]。

其它的信号量操作函数OSSem?

?

?

()通过检查该域来保证所操作的任务控制块类型的正确。

例如,这可以防止调用OSSemPost()函数对一个用作邮箱的任务控制块进行操作[7.06节,邮箱]。

接着,用信号量的初始值对任务控制块进行初始化[L7.9(5)],并调用OSEventWaitListInit()函数对事件控制任务控制块的等待任务列表进行初始化[见6.01节,初始化一个任务控制块,OSEventWaitListInit()][L7.9(6)]。

因为信号量正在被初始化,所以这时没有任何任务等待该信号量。

最后,OSSemCreate()返回给调用函数一个指向任务控制块的指针。

以后对信号量的所有操作,如OSSemPend(),OSSemPost(),OSSemAccept()和OSSemQuery()都是通过该指针完成的。

因此,这个指针实际上就是该信号量的句柄。

如果系统中没有可用的任务控制块,OSSemCreate()将返回一个NULL指针。

程序清单L7.9建立一个信号量

OS_EVENT*OSSemCreate(INT16Ucnt)

{

#ifOS_CRITICAL_METHOD==3

OS_CPU_SRcpu_sr;

#endif

OS_EVENT*pevent;

if(OSIntNesting>0){

*err=OS_ERR_CREATE_ISR;

return((OS_EVENT*)0);

}

OS_ENTER_CRITICAL();

pevent=OSEventFreeList;

(1)

if(OSEventFreeList!

=(OS_EVENT*)0){

(2)

OSEventFreeList=(OS_EVENT*)OSEventFreeList->OSEventPtr;

}

OS_EXIT_CRITICAL();

if(pevent!

=(OS_EVENT*)0){(3)

pevent->OSEventType=OS_EVENT_TYPE_SEM;(4)

pevent->OSEventCnt=cnt;(5)

OSEventWaitListInit(pevent);(6)

}

return(pevent);(7)

}

7.01删除一个信号量,OSSemDel()

删除一个信号量,OSSemDel()

OS_EVENT*OSSemDel(OS_EVENT*pevent,INT8Uopt,INT8U*err)

{

#ifOS_CRITICAL_METHOD==3

OS_CPU_SRcpu_sr;

#endif

BOOLEANtasks_waiting;

if(OSIntNesting>0){

*err=OS_ERR_DEL_ISR;

return(pevent);;

}

#ifOS_ARG_CHK_EN>0

if(pevent==(OS_EVENT*)0){

*err=OS_ERR_PEVENT_NULL;

return(pevent);

}

if(pevent->OSEventType!

=OS_EVENT_TYPE_SEM){

*err=OS_ERR_EVENT_TYPE;

return(pevent);

}

#endif

OS_ENTER_CRITICAL();

if(pevent->OSEventGrp!

=0x00){

tasks_waiting=TRUE;

}else{

tasks_waiting=FALSE;

}

switch(opt){

caseOS_DEL_NO_PEND:

if(tasks_waiting==FALSE){

pevent->OSEventType=OS_EVENT_TYPE_UNUSED;

pevent->OSEventPtr=OSEventFreeList;

OSEventFreeList=pevent;

OS_EXIT_CRITICAL();

*err=OS_NO_ERR;

return((OS_EVENT*)0);

}

else{

OS_EXIT_CRITICAL();

*err=OS_ERR_TASK_WAITING;

return(pevent);

}

caseOS_DEL_ALWAYS:

while(pevent->OSEventGrp!

=0x00){

OS_EventTaskRdy(pevent,(void*)0,OS_STAT_SEM);

}

pevent->OSEventType=OS_EVENT_TYPE_UNUSED;

pevent->OSEventPtr=OSEventFreeList;

OSEventFreeList=pevent;

OS_EXIT_CRITICAL();

if(tasks_waiting==TRUE){

OS_Sched();

}

*err=OS_NO_ERR;

return((OS_EVENT*)0);

default:

OS_EXIT_CRITICAL();

*err=OS_ERR_INVALID_OPT;

return(pevent);

}

}

7.02等待一个信号量,OSSemPend()

程序清单L7.10是OSSemPend()函数的源代码。

它首先检查指针pevent所指的任务控制块是否是由OSSemCreate()建立的[L7.10

(1)]。

如果信号量当前是可用的(信号量的计数值大于0)[L7.10

(2)],将信号量的计数值减1[L7.10(3)],然后函数将“无错”错误代码返回给它的调用函数。

显然,如果正在等待信号量,这时的输出正是我们所希望的,也是运行OSSemPend()函数最快的路径。

如果此时信号量无效(计数器的值是0),OSSemPend()函数要进一步检查它的调用函数是不是中断服务子程序[L7.10(4)]。

在正常情况下,中断服务子程序是不会调用OSSemPend()函数的。

这里加入这些代码,只是为了以防万一。

当然,在信号量有效的情况下,即使是中断服务子程序调用的OSSemPend(),函数也会成功返回,不会出任何错误。

如果信号量的计数值为0,而OSSemPend()函数又不是由中断服务子程序调用的,则调用OSSemPend()函数的任务要进入睡眠状态,等待另一个任务(或者中断服务子程序)发出该信号量(见下节)。

OSSemPend()允许用户定义一个最长等待时间作为它的参数,这样可以避免该任务无休止地等待下去。

如果该参数值是一个大于0的值,那么该任务将一直等到信号有效或者等待超时。

如果该参数值为0,该任务将一直等待下去。

OSSemPend()函数通过将任务控制块中的状态标志.OSTCBStat置1,把任务置于睡眠状态[L7.10(5)],等待时间也同时置入任务控制块中[L7.10(6)],该值在OSTimeTick()函数中被逐次递减。

注意,OSTimeTick()函数对每个任务的任务控制块的.OSTCBDly域做递减操作(只要该域不为0)[见3.10节,时钟节拍]。

真正将任务置入睡眠状态的操作在OSEventTaskWait()函数中执行[见7.03节,让一个任务等待某个事件,OSEventTaskWait()][L7.10(7)]。

因为当前任务已经不是就绪态了,所以任务调度函数将下一个最高优先级的任务调入,准备运行[L7.10(8)]。

当信号量有效或者等待时间到后,调用OSSemPend()函数的任务将再一次成为最高优先级任务。

这时OSSched()函数返回。

这之后,OSSemPend()要检查任务控制块中的状态标志,看该任务是否仍处于等待信号量的状态[L7.10(9)]。

如果是,说明该任务还没有被OSSemPost()函数发出的信号量唤醒。

事实上,该任务是因为等待超时而由TimeTick()函数把它置为就绪状态的。

这种情况下,OSSemPend()函数调用OSEventTO()函数将任务从等待任务列表中删除[L7.10(10)],并返回给它的调用任务一个“超时”的错误代码。

如果任务的任务控制块中的OS_STAT_SEM标志位没有置位,就认为调用OSSemPend()的任务已经得到了该信号量,将指向信号量ECB的指针从该任务的任务控制块中删除,并返回给调用函数一个“无错”的错误代码[L7.10(11)]。

程序清单L7.10等待一个信号量

voidOSSemPend(OS_EVENT*pevent,INT16Utimeout,INT8U*err)

{

#ifOS_CRITICAL_METHOD==3

OS_CPU_SRcpu_sr;

#endif

if(OSIntNesting>0){

*err=OS_ERR_PEND_ISR;

return;

}

#ifOS_ARG_CHK_EN>0

if(pevent==(OS_EVENT*)0){

*err=OS_ERR_PEVENT_NULL;

return;

}

if(pevent->OSEventType!

=OS_EVENT_TYPE_SEM){

*err=OS_ERR_EVENT_TYPE;

return;

}

#endif

OS_ENTER_CRITICAL();

if(pevent->OSEventCnt>0){

(2)

pevent->OSEventCnt--;(3)

OS_EXIT_CRITICAL();

*err=OS_NO_ERR;

}elseif(OSIntNesting>0){(4)

OS_EXIT_CRITICAL();

*err=OS_ERR_PEND_ISR;

}else{

OSTCBCur->OSTCBStat|=OS_STAT_SEM;(5)

OSTCBCur->OSTCBDly=timeout;(6)

OSEventTaskWait(pevent);(7)

OS_EXIT_CRITICAL();

OSSched();(8)

OS_ENTER_CRITICAL();

if(OSTCBCur->OSTCBStat&OS_STAT_SEM){(9)

OSEventTO(pevent);(10)

OS_EXIT_CRITICAL();

*err=OS_TIMEOUT;

}else{

OSTCBCur->OSTCBEventPtr=(OS_EVENT*)0;(11)

OS_EXIT_CRITICAL();

*err=OS_NO_ERR;

}

}

}

7.03发出一个信号量,OSSemPost()

程序清单L7.11是OSSemPost()函数的源代码。

它首先检查参数指针pevent指向的任务控制块是否是OSSemCreate()函数建立的[L7.11

(1)],接着检查是否有任务在等待该信号量[L7.11

(2)]。

如果该任务控制块中的.OSEventGrp域不是0,说明有任务正在等待该信号量。

这时,就要调用函数OSEventTaskRdy()[见7.02节,使一个任务进入就绪状态,OSEventTaskRdy()],把其中的最高优先级任务从等待任务列表中删除[L7.11(3)]并使它进入就绪状态。

然后,调用OSSched()任务调度函数检查该任务是否是系统中的最高优先级的就绪任务[L7.11(4)]。

如果是,这时就要进行任务切换[当OSSemPost()函数是在任务中调用的],准备执行该就绪任务。

如果不是,OSSched()直接返回,调用OSSemPost()的任务得以继续执行。

如果这时没有任务在等待该信号量,该信号量的计数值就简单地加1[L7.11(5)]。

上面是由任务调用OSSemPost()时的情况。

当中断服务子程序调用该函数时,不会发生上面的任务切换。

如果需要,任务切换要等到中断嵌套的最外层中断服务子程序调用OSIntExit()函数后才能进行(见3.09节,µC/OS-II中的中断)。

程序清单L7.11发出一个信号量

INT8UOSSemPost(OS_EVENT*pevent)

{

#ifOS_CRITICAL_METHOD==3

OS_CPU_SRcpu_sr;

#endif

#ifOS_ARG_CHK_EN>0

if(pevent==(OS_EVENT*)0){

return(OS_ERR_PEVENT_NULL);

}

if(pevent->OSEventType!

=OS_EVENT_TYPE_SEM){

return(OS_ERR_EVENT_TYPE);

}

#endif

OS_ENTER_CRITICAL();

if(pevent->OSEventGrp){

(2)

OSEventTaskRdy(pevent,(void*)0,OS_STAT_SEM);(3)

OS_EXIT_CRITICAL();

OSSched();(4)

return(OS_NO_ERR);

}else{

if(pevent->OSEventCnt<65535){

pevent->OSEventCnt++;(5)

OS_EXIT_CRITICAL();

return(OS_NO_ERR);

}else{

OS_EXIT_CRITICAL();

return(OS_SEM_OVF);

}

}

}

7.04无等待地请求一个信号量,OSSemAccept()

当一个任务请求一个信号量时,如果该信号量暂时无效,也可以让该任务简单地返回,而不是进入睡眠等待状态。

这种情况下的操作是由OSSemAccept()函数完成的,其源代码见程序清单L7.12。

该函数在最开始也是检查参数指针pevent指向的事件控制块是否是由OSSemCreate()函数建立的[L7.12

(1)],接着从该信号量的事件控制块中取出当前计数值[L7.12

(2)],并检查该信号量是否有效(计数值是否为非0值)[L7.12(3)]。

如果有效,则将信号量的计数值减1[L7.12(4)],然后将信号量的原有计数值返回给调用函数[L7.12(5)]。

调用函数需要对该返回值进行检查。

如果该值是0,说明该信号量无效。

如果该值大于0,说明该信号量有效,同时该值也暗示着该信号量当前可用的资源数。

应该注意的是,这些可用资源中,已经被该调用函数自身占用了一个(该计数值已经被减1)。

中断服务子程序要请求信号量时,只能用OSSemAccept()而不能用OSSemPend(),因为中断服务子程序是不允许等待的。

程序清单L7.12无等待地请求一个信号量

INT16UOSSemAccept(OS_EVENT*pevent)

{

#ifOS_CRITICAL_METHOD==3

OS_CPU_SRcpu_sr;

#endif

INT16Ucnt;

#ifOS_ARG_CHK_EN>0

if(pevent==(OS_EVENT*)0){

*err=OS_ERR_PEVENT_NULL;

return;

}

if(pevent->OSEventType!

=OS_EVENT_TYPE_SEM){

*err=OS_ERR_EVENT_TYPE;

return;

}

#endif

OS_ENTER_CRITICAL();

cnt=pevent->OSEventCnt;

(2)

if(cnt>0){(3)

pevent->OSEventCnt--;(4)

}

OS_EXIT_CRITICAL();

return(cnt);(5)

}

7.05查询一个信号量的当前状态,OSSemQuery()

在应用程序中,用户随时可以调用函数OSSemQuery()[程序清单L7.13]来查询一个信号量的当前状态。

该函数有两个参数:

一个是指向信号量对应事件控制块的指针pevent。

该指针是在生产信号量时,由OSSemCreate()函数返回的;另一个是指向用于记录信号量信息的数据结构OS_SEM_DATA(见uCOS_II.H)的指针pdata。

因此,调用该函数前,用户必须先定义该结构变量,用于存储信号量的有关信息。

在这里,之所以使用一个新的数据结构的原因在于,调用函数应该只关心那些和特定信号量有关的信息,而不是象OS_EVENT数据结构包含的很全面的信息。

该数据结构只包含信号量计数值.OSCnt和等待任务列表.OSEventTbl[]、.OSEventGrp,而OS_EVENT中还包含了另外的两个域.OSEventType和.OSEventPtr。

和其它与信号量有关的函数一样,OSSemQuery()也是先检查pevent指向的事件控制块是否是OSSemCreate()产生的[L7.13

(1)],然后将等待任务列表[L7.13

(2)]和计数值[L7.13(3)]从OS_EVENT结构拷贝到OS_SEM_DATA结构变量中去。

程序清单L7.13查询一个信号量的状态

INT8UOSSemQuery(OS_EVENT*pevent,OS_SEM_DATA*pdata)

{

#ifOS_CRITICAL_METHOD==3

OS_CPU_SRcpu_sr;

#endif

INT8Ui;

INT8U*psrc;

INT8U*pdest;

#ifOS_ARG_CHK_EN>0

if(pevent==(OS_EVENT*)0){

*err=OS_ERR_PEVENT_NULL;

return;

}

if(pevent->OSEventType!

=OS_EVENT_TYPE_SEM){

*err=OS_ERR_EVENT_TYPE;

return;

}

#endif

OS_ENTER_CRITICAL();

pdata->OSEventGrp=pevent->OSEventGrp;

psrc=&pevent->OSEventTbl[0];

pdest=&pdata->OSEventTbl[0];

for(i=0;i

*pdest++=*psrc++;

}

pdata->OSCnt=pevent->OSEventCnt;(3)

OS_EXIT_CRITICAL();

return(OS_NO_ERR);

}

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > PPT模板 > 卡通动漫

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1