您的位置:首页 > 其它

uC/OS-II的任务同步与通信

2014-11-18 16:03 330 查看
多任务合作过程中的,操作系统应解决两个问题:一是各任务之间应具有一种互斥关系,即对于某个共享资源的共享,如果一个任务正在使用,则其他任务只能等待,等到该任务释放该资源以后,等待的任务之一才能使用它;二是相关的任务在执行上要有先后次序,一个任务要等其伙伴发来通知或建立了某个条件后才能继续执行,否则只能等待。

任务之间的这种制约性的合作运行机制叫做任务间的同步

事件

通信就要依赖中间媒介。在uC/OS-II中,使用信号量,邮箱(消息邮箱)和消息队列这些数据结构来作为中间媒介。由于这些数据结构将要影响到任务的程序流程,所以它们也被称为事件

把信息发送到事件上的操作叫做发送事件,读取事件的操作叫做请求事件,或者叫做等待事件

信号量

信号量是一类用来进行任务间通信的最基本事件。由于二值事件可以实现共享资源的独占式占用,所以叫做互斥型信号量。计数式的信号叫做信号量

给等待信号量的任务设置一个等待时限。若等待信号量的任务因等待时间已超过这个时限却还未等到这个信号,则令该任务脱离等待状态而继续运行,这样就不会出现死机现象。

在严格按照优先级别进行调度的可剥夺内核中,优先级别决定了任务能否获得处理器的使用权,而能否获得信号量则决定它能否被运行。也就是说,在使用了信号量进行同步的任务中,制约任务能否运行的条件有两个:一个是它的优先级别;另一个是它是否获得了它正在等待的信号量。正是这个事实,产生了不得不想办法解决的优先级反转问题;也正是这个事实,使得操作系统的C/S结构得以实现。

消息邮箱

在多任务操作系统中,常常需要通过传递一个数据(这种数据叫做消息)的方式来进行任务之间的通信。为了达到这个目的,可以在内存中创建一个存储空间作为该数据的缓冲区。如果把这个缓冲区叫做消息缓冲区,那么在任务间传递数据(消息)的一个最简单的方法就是传递消息缓冲区。于是,用来传递消息缓冲区指针的数据结构(事件)就叫做消息邮箱。

消息队列

上面的消息邮箱不仅可用来传递一个消息,而且也可以定义一个指针数组。让数组的每个元素都存放一个消息缓冲区指针,那么任务就可以通过传递这个指针数组指针的方法来传递多个消息了。这种可以传递多个消息的数据结构叫做消息队列。

事件的等待任务列表

作为功能完善的事件,应有对这些等待任务具有两方面的管理功能:一是要对等待事件的所有任务进行记录并排序;二是允许任务有一定的等待时限

对于等待事件任务的记录和排序,uC/OS-II采用了与任务就绪表类似的方法,定义一个INT8U类型的数组OSEventTbl[]作为记录等待事件任务的记录表,这个表叫做等待任务表。也定义一个INT8U的变量OSEventGrp来表示等待任务表中的任务组。

至于等待任务的等待时限,则记录在等待任务的任务控制块TCB的成员OSTCBDly中,并在每个时钟节拍中断服务程序中对该数据进行维护。每当有任务的等待时限已到时,则将该任务从事件等待任务表中删除,并使它进入就绪状态。

事件控制块

uC/OS-II把事件等待任务表和与事件相关的其它信息组合起来定义一个叫做事件控制块ECB的数据结构。这样在uC/OS-II中统一采用ECB来描述注入信号量,邮箱(消息邮箱)和消息队列这些事件。

在uC/OS-II.H中,使劲按控制块的定义如下:

#if (OS_MAX_EVENTS >= 2)

typedef struct {

void *OSEventPtr;

INT8U OSEventTbl[OS_EVENT_TBL_SIZE];

INT16U OSEventCnt;

INT8U OSEventType;

INT8U OSEventGrp;

} OS_EVENT;

#endif

成员OSEventTbl[OS_EVENT_TBL_SIZE]是一个数组,与任务就绪表的格式一样。应用程序中的所有任务按照优先级别各自在表中占据一个二进制位,并用该位的值是1还是0来表示该位对应的任务是否为正在等待事件的任务,这个表被叫做任务等待表。

事件控制块的基本函数操作

uC/OS-II有4个对事件控制块进行基本操作的函数(定义在文件OS_CORE.C中),以供操作信号量,消息邮箱,消息队列等事件的函数来调用。

1) 事件控制块的初始化函数

#if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN || OS_SEM_EN

void OSEventWaitListInit (OS_EVENT *pevent)//指向事件控制块的指针

该函数将在任务调用OS***Create()函数创建事件时,被OS***Create()函数所调用。这里***代表:Sem,Mutex,Mbox,Q。

2) 使一个任务进入等待状态的函数

把一个任务置于等待状态要调用OSEventTaskWait()函数

#if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN || OS_SEM_EN

void OSEventTaskWait (OS_EVENT *pevent)

该函数将在任务调用函数OS***Pend()请求一个事件时,被OS***Pend()所调用。

3) 使一个正在等待任务进入就绪状态的函数

调用函数OSEventTaskRdy(),把调用这个函数的任务在任务等待表中的位置清0(解除等待状态)后,再把任务在任务就绪表中的对应位置1,然后引发一次任务调度。

#if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN || OS_SEM_EN

void OSEventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk)

该函数将在任务调用函数OS***Post()发送一个事件时,被函数OS***Post()调用。

4) 使一个等待超时的任务进入就绪状态的函数

如果一个正在等待事件的任务已经超过了等待的时间,却仍由于么有获取事件等原因而具备可以运行的条件,却又要使它进入就绪状态,这时要调用函数OSEventTO()。

#if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN || OS_SEM_EN

void OSEventTO (OS_EVENT *pevent)

该函数将在任务调用OS***Pend()请求一个事件时,被OS***Pend()所调用。

空事件控制块链表

与管理任务控制块的方法类似,uC/OS-II把事件控制块也组织成为两条链表来管理。

在uC/OS-II初始化时,系统会在初始化函数OSInit()中按照应用程序使用事件的总数OS_MAX_EVENTS(OS_CFG.H),创建OS_MAX_EVENTS个空事件块,并借用成员OSEventPtr作为链接指针,组成单向链表。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: