《任务的同步与通信修改的ppt课件.ppt》由会员分享,可在线阅读,更多相关《任务的同步与通信修改的ppt课件.ppt(70页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、第三章 任务管理3.3 任务同步与通信3.2 多任务3.1 任务描述和状态,控制 3.3任务的同步与通信1.任务的同步和事件2.事件控制块及事件处理函数3.信号量及其操作4.消息邮箱及其操作5.消息队列及其操作1.任务的同步和事件嵌入式系统中的各个任务是为同一个大的任务服务的子任务,它们不可避免地要共同使用一些共享资源,并且在处理一些需要多任务共同协作来完成的工作时,还需要相互的支持和限制。因此,对于一个完善的多任务操作系统来说,系统必须具有完备的同步和通信机制任务间的同步为了实现各个任务之间的合作和无冲突运行,在各任务之间必须建立一些约束关系l直接制约关系l间接制约关系任务间的同步-直接制约
2、关系直接制约关系源于任务之间的合作例如:有任务A和任务B两个任务,他们需要访问同一个数据缓冲区合作完成一项任务任务A任务B缓冲区写入数据读出数据任务A为向缓冲区写入数据时,任务B因不能从缓冲区得到有效数据而应该处于等待状态只有等任务A向缓冲区写入了数据之后,才应该通知任务B去取数据当缓冲区的数据还未被任务B读取时,任务A就不能像缓冲区写入新的数据而应该处于等待状态只有等任务B自缓冲区读取数据后,才应该通知任务A写入数据任务间的同步-间接制约关系间接制约关系源于对资源的共享例如:任务A和任务B共享一台打印机任务A任务B打印机如果系统已经把打印机分配给任务A,则任务B因不能获得打印机的使用权而应该
3、处于等待状态只有当任务A把打印机释放后,系统才能唤醒任务B使其获得打印机的使用权。因此,在多任务合作工作的过程中,操作系统应该解决两个问题:l各任务间应该具有一种互斥关系,即对于某个共享资源,如果一个任务正在使用,则其他任务只能等待,等该任务释放该资源后,等待的任务之一才能使用它l相关的任务在执行上要有先后次序,一个任务要等其伙伴发来通知,或建立了某个条件后才能继续执行,否则只能等待任务之间的这种制约性的合作运行机制叫做任务间的同步事件uC/os-II使用信号量,邮箱(消息邮箱)和消息队列这些中间环节来实现任务之间的通信。为了方便起见,这些中间环节都统一被称作“事件任务1任务2事件发送事件请求
4、事件如上图:任务1是发信方,任务2是收信方作为发信方,任务1的责任是把信息发送到事件上,这项操作叫做发送事件作为收信方,任务2的责任是通过读事件操作对事件事件进行查询:如果有信息,则读取信息;否则等待。读事件操作叫做请求事件uC/os-II把任务发送事件,请求事件以及其他对事件的操作都定义成为全局函数,以供应用程序的所有任务来调用信号量信号量是一类事件。使用信号量的最初目的,是为了给共享资源设立一个标志,该标识表示该共享资源被占用的情况。这样,当一个任务在访问共享资源之前,就可以先对这个标志进行查询,从而在了解资源被占用的情况之后,再来决定自己的行为两个任务正在使用互斥型信号量进行通信任务1任
5、务21 0信号量共享资源先请求信号量后请求信号量任务1在访问共享资源之前先进性请求信号量操作,当任务1发现信号量的标志为“1”时,它一方面把信号量的标志由“1”改为“0”,另一方面进行共享资源的访问任务2在任务1已经获得信号之后来请求信号量,那么由于它获得的标志值是“0”,所以任务2只能等待而不能访问共享资源任务1任务20 1 0信号量共享资源发送信号量请求信号量任务1使用完共享资源之后,由任务1向信号量发信号使信号量标志的值由0变为1,任务2就有机会访问共享资源。任务2一旦获得了共享资源的访问权,那么在访问共享资源之前一定要把信号量标志值由1变为0消息邮箱在多任务操作系统中,常常需要在任务与
6、任务之间通过传递一个数据(这种数据叫做“消息”)的方式来进行通信为了达到这个目的,可以在内存中创建一个存储空间作为该数据的缓冲区把这个缓冲区叫做消息缓冲区,在任务间传递数据一个最简单的方法就是传递消息缓冲区的指针。用来传递消息缓冲区指针的数据结构就叫做消息邮箱任务1任务2消息缓冲区指针消息邮箱 发送消息(发送消息缓冲区指针) 请求消息(读取消息缓冲区指针)消息队列上面谈到的消息邮箱不仅可以用来传递一个消息,而且可以定义一个指针数组。让数组的每个元素都存放一个消息缓冲区指针,那么任务就可通过传递这个指针数组指针的方法来传递多个消息了。这种可以传递多个消息的数据结构叫做消息队列任务1任务2消息缓冲
7、区2消息缓冲区1消息缓冲区n指针消息队列 . . . .发送消息队列(发送消息缓冲区指针数组的指针)请求消息队列(读取消息缓冲区指针数组的指针)2.事件控制块及事件处理函数对于事件来说,当一个事件被占用时,会导致其他请求该事件的任务因暂时得不到事件的服务而处于等待状态。作为功能完善的事件,应该对这些等待任务具有两方面的管理功能:l一是要对等待事件的所有任务进行记录并排序l二是应该允许等待任务有一个等待时限,即当任务认为等不及时可以退出对事件的请求对于等待事件任务的记录,uC/os-II又使用了与任务就绪表类似的位图,即定义了一个INT8U类型的数组OSEventTbl来作为等待事件任务的记录表
8、,即等待任务表等待任务表仍然以任务的优先级别为顺序为每个任务分配一个二进制位,并用该位为“1”来表示这一位对应的任务位事件的等待任务,否则不是等待任务同样,为了加快对该表的访问速度,也定义了一个INT8U类型的变量OSEventGrp来表示等待任务表中的任务组。1/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01/01
9、/01/01/01/01/01/01/01/0OSEventGrp任务等待表OSEventTbl事件控制块的结构为了把描述事件的数据结构统一起来uC/os-II使用叫做事件控制块ECB的数据结构来描述诸如信号量,邮箱(消息邮箱)和消息队列这些事件typedef structlINT8U OSEventType;/事件的类型lINT16U OSEventCnt;/信号量计数器lvoid *OSEventPtr;/消息或消息队列的指针lINT8U OSEventPtr;/等待事件的任务组lINT8U OSEventTblOS_EVENT_TBL_SIZE;l/事件等待表操作事件控制块的函数uC/o
10、s-II有4个对事件控制块进行基本操作的函数(定义在文件OS_CORE.C),以供操作信号量,消息邮箱,消息队列等事件的函数来调用1.事件控制块初始化函数void OS_EventWaitListInit(lOS_EVENT *pevent /事件控制块指针);函数作用:把变量OSEventGrp及任务等待表中的每一位都清0,即令事件的任务等代表中不含任何等待任务2.使一个任务进入等待状态的函数void OS_EventTaskWait(lOS_EVENT *pevent /事件控制块指针);将在任务调用函数OS*Pend()请求一个事件时被函数OS*Pend()所调用3.使一个正在等待的任务
11、进入就绪状态INT8U OS_EventTaskRdy(lOS_EVENT *pevent, /事件控制块指针lvoid *msg, /未使用lINT8U msk/清除TCB状态标志掩码);将在任务调用函数OS*Post()发送一个事件时被函数OS*Post()调用4.使一个等待超时的任务进入就绪状态void OS_EventTO(lOS_EVENT *pevent /事件控制块指针);如果一个正在等待事件的任务已经超过了等待的时间,却仍因为没有获取事件等原因而为具备可以运行的条件,却又要使他进入就绪状态,这是调用函数OS_EventTO()3.信号量及其操作1.创建信号量OS_EVENT *
12、OSSemCreate(lINT16U cnt /信号量计数器初始值);返回值为已创建的信号量的指针2.请求信号量void OSSemPend(lOS_EVENT *pevent,/信号量的指针lINT16U timeout, /等待时限lINT8U *err /错误信息);为防止任务因得不到信号量而处于长期的等待状态,函数允许参数timeout设置一个等待事件的限制。当任务等待事件超过timeout,可以结束等待状态而进入就绪状态。如果该参数设置为0,表明任务的等待时间为无限长函数调用成功后,err的值为OS_NO_ERR.如果函数调用失败,则函数会根据在函数中出现的具体错误,令err的值分
13、别为OS_ERR_PEND_ISR,OS_ERR_PEVENT_NULL,OS_ERR_EVENT_TYPE和OS_TIMEOUT当任务需要访问一个共享资源时,先要请求管理该资源的信号量,这时就可以根据信号量当前是否有效(即信号量计数器OSEventCnt是否大于0)来决定该任务是否可以继续运行如果信号量有效,则把信号量计数器-1,然后继续运行任务如果无效,则会在等待任务表中对应的位置1而让任务处于等待状态,并把等待时限timeout保存在TCB的OSTCBDly中当一个任务请求信号量时,如果希望在信号量无效时准许任务不进入等待状态而继续运行,则调用OSSemAccept()INT16U OS
14、SemAccept(lOS_EVENT *pevent,/信号量的指针);3.发送信号量任务获得信号量,并在访问共享资源结束后,必须释放信号量。释放信号量也叫做发送信号量。发送信号量调用OSSemPost().该函数在对信号量计数器操作之前,首先要检查是否还有等待该信号量的任务;如果没有,则将该信号量计数器OSEventCnt+1;如果有,则调用调度器OS_Sched()去运行等待任务中优先级最高的任务INT8U OSSemPost(lOS_EVENT *pevent,/信号量的指针);4.删除信号量OS_EVENT *OSSemDel(lOS_EVENT *pevent,/信号量的指针lIN
15、T8U opt , /删除条件选项lINT8U *err /错误信息);函数中的参数opt用来指明信号量的删除条件。该参数有两个值可以选择:lOS_DEL_NO_PEND:当等待任务列表中没有等待任务时删除信号量lOS_DEL_ALLWAYS:无论等待任务列表中是否有等待任务都删除信号量函数调用成功后,err值为OS_NO_ERR需要注意的是,只能在任务中删除信号量,而不能在中断服务程序中删除5.查询信号量的状态INT8U OSSemQuery(lOS_EVENT *pevent,/信号量的指针lOS_SEM_DATA *pdata /存储信号量状态的结构);任务调用函数对信号量查询会,会把信
16、号量中的相关信息存储到OS_SEM_DATA类型的变量中,因此调用该函数之前,须定义一个OS_SEM_DATA结构类型的变量函数调用成功后,返回值为OS_NO_ERR4.消息邮箱及其操作消息邮箱通过在两个需要的任务之间传递数据缓冲区指针来进行通信1.创建消息邮箱OS_EVENT *OSMboxCreate(lvoid *msg /消息指针)函数返回消息邮箱指针2.向消息邮箱发送信息INT8U OSMboxPost(lOS_EVENT *pevent, /消息邮箱指针lvoid *msg /消息指针l);其中第二个参数为消息缓冲区指针;函数返回值未错误号INT8U OSMboxPostOpt(l
17、OS_EVENT *pevent,/消息邮箱指针lvoid *msg,/消息指针lINT8U opt/广播选项);该函数可以用广播的方式向事件等待任务表中所有任务发送消息3.请求消息邮箱void *OSMboxPend(lOS_EVENT *pevent,/请求消息邮箱指针lINT16U timeout,/等待时限lINT8U *err /错误信息);主要作用就是查看邮箱指针OSEventPtr是否为NULL。如果不是NULL,则把邮箱中的消息指针返回给调用函数的任务,当函数参数err为OS_NO_ERR时,表示任务取消息成功;如果邮箱指针OSEventPtr是NULL,则使任务进入等待状态,
18、并引发一次调度void *OSMboxAccept(lOS_EVENT *pevent /消息邮箱指针);该函数不同于OSMboxPend()的区别在于,调用函数OSMboxAccept()失败时,任务不进行等待而继续运行。改函数的返回值为消息指针4.查询邮箱状态INT8U OSMboxQuery(lOS_EVENT *pevent,/消息邮箱指针lOS_MBOX_DATA *pdata /存放邮箱信息的结构);5.删除邮箱OS_EVENT *OSMboxDel(lOS_EVENT *pevent, /消息邮箱指针lINT8U opt, /删除选项lINT8U *err /错误信息);5.消息
19、队列及其操作1.创建消息队列OS_EVENT OSQCreate(lvoid * * start, /指针数组的地址lINT16U size /数组长度);函数首先从空闲队列控制块链表摘取一个控制块并按参数start和size填写诸项,然后把消息队列初始化为空2.请求消息队列void OSQPend(lOS_EVENT *pevent,/所请求的消息队列的指针lINT16U timeout,/等待时限lINT8U * err /错误信息);函数需要通过访问事件控制块的成员OSEventPtr指向的队列控制块OS_Q的成员OSQEntries来判断是否有消息可用,如果有消息可用,则返回OS_Q成
20、员OSQOut指向的消息,通知调整指针OSQOut,使之指向下一条消息并把有效消息数的变量OSQEntries-1;如果无消息可用,则使用调用函数OSQPend()的任务挂起,使之处于等待状态并引发一次任务调度如果希望任务无等待地请求一个消息队列,则可调用函数OSQAccept()void OSQAccept(lOS_EVENT *pevent /请求的消息队列指针);3.向消息队列发送信息任务需要通过调用函数OSQPost()或OSQPostFront()来向消息队列发送信息。OSQPost()以FIFO的方式组织消息队列OSQPostFront()以LIFO的方式组织消息队列INT8U O
21、SQPost(lOS_EVENT *pevent ,/消息队列的指针lvoid *msg /消息指针);INT8U OSQPost Front(lOS_EVENT *pevent ,/消息队列的指针lvoid *msg /消息指针);如果任务希望以广播的方式通过消息队列发送消息,则需要调用函数OSQPostOpt()INT8U OSQPostOpt(lOS_EVENT *pevent ,/消息队列的指针lvoid *msg /消息指针lINT8U opt/广播选项);4.清空消息队列INT8U OSQFlush(lOS_EVENT *pevent /消息队列指针);5.删除消息队列OS_EVE
22、NT *OSQDel(lOS_EVENT *pevent /消息队列指针);6.查询消息队列INT8U OSQQuery(lOS_EVENT *pevent /消息队列指针lOS_Q_DATA *pdata /存放状态信息的结构);uC/os-II初始化在调用uC/os-II的任何其他服务之前,uC/os-II要求用户首先调用系统初始化函数OSInit()。OSInit()初始化uC/os-II所有的变量和数据结构OSInit()建立空闲任务idle task,这个任务总是处于就绪态的。空闲任务OSTaskIdle()的优先级总是设成最低,即OS_LOWEST_PRIO。如果统计任务允许OS_
23、TASK_STAT_EN和任务建立扩展允许都设为1,则OSInit()还得建立统计任务OSTaskStat()并且让其进入就绪态。OSTaskStat的优先级总是设为OS_LOWEST_PRIO0-1如上图所示,uC/os-II还初始化了4个空数据结构缓冲区。每个缓冲区都是单向链表,允许uC/os-II从缓冲区中迅速得到或释放一个缓冲区的元素空任务控制块在空缓冲区中的数目取决于最多任务数OS_N_SYS_TASKS。控制块OS_TCB的数目也就自动确定了初始化和启动uC/os-II当调用OSStart()时,OSStart()从任务就绪表中找出那个用户建立的优先级最高任务的任务控制块。然后调用
24、高优先级就绪任务启动函数OSStartHighRdy()。这个文件与选择的微处理器有关。实质上,函数OSStartHighRdy()是将任务栈中保存的值弹回到CPU寄存器中,然后执行一条中断返回指令,中断返回指令强制执行该任务代码启动多任务多任务启动以后变量与数据结构中的内容如3.9所示。这里假设用户建立的任务优先级为6OSTaskCtr指出已经建立了3个任务。OSRunning已设为“真”,指出多任务已经开始lOSPrioCur和OSPrioHighRdy存放的是用户应用任务的优先级lOSTCBCur和OSTCBHighRdy两者都指向用户任务的任务控制块获取当前uC/os-II 版本号应用程序调用OSVersion()可以得到当前uC/os-II的版本号。OSVersion(0函数返回版本号值乘以100。即200表示版本号2.00INT16U OSVersion (void)lreturn (OS_VERSION);l