μC/OS-II 任务的同步与通信 --- 信号量

任务间通信

系统中的多个任务在运行时,经常需要互相无冲突地访问同一个共享资源,或者需要互相支持和依赖,甚至有时还要互相加以必要的限制和制约,才保证任务的顺利运行。因此,操作系统必须具有对任务的运行进行协调的能力,从而使任务之间可以无冲突、流畅地同步运行,而不致导致灾难性的后果。
与人们依靠通信来互相沟通,从而使人际关系和谐、工作顺利的做法一样,计算机系统是依靠任务之间的良好通信来保证任务与任务的同步的。

举例说明

两个任务:任务 A 和任务 B,它们需要通过访问同一个数据缓冲区合作完成一项工作,任务 A 负责向缓冲区写入数据,任务 B 负责从缓冲区读取该数据。显然,当任务A还未向缓冲区写入数据时(缓冲区为空时),任务 B 因不能从缓冲区得到有效数据而应该处于等待状态,只有等任务 A 向缓冲区写入了数据之后,才应该通知任务 B 去取数据。

事件控制块

为了把描述事件的数据结构统一起来,μC/OS-II 使用叫做事件控制块 ECB 的数据结构来描述诸如信号量、邮箱(消息邮箱)和消息队列这些事件。事件控制块中包含包括等待任务表在内的所有有关事件的数据。

typedef struct 
{
   INT8U  OSEventType;          //事件的类型
   INT16U OSEventCnt;           //信号量计数器
   void *OSEventPtr;            //消息或消息队列的指针
   INT8U  OSEventGrp;           //等待事件的任务组
   INT8U OSEventTbl[OS_EVENT_TBL_SIZE];//任务等待表
} OS_EVENT;

μC/OS-II 任务的同步与通信 --- 信号量_第1张图片

把一个任务置于等待状态要调用 OS_EventTaskWait( ) 函数。该函数的原型为:

void  OS_EventTaskWait (
OS_EVENT *pevent    //事件控制块的指针
);

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

空事件链表

在 μC/OS-II 初始化时,系统会在初始化函数 OSInit( ) 中按应用程序使用事件的总数 OS_MAX_EVENTS(在文件OS_CFG.H中定义),创建 OS_MAX_EVENTS 个空事件控制块并借用成员 OSEventPtr 作为链接指针,把这些空事件控制块链接成一个单向链表。由于链表中的所有控制块尚未与具体事件相关联,故该链表叫做空事件控制块链表。

μC/OS-II 任务的同步与通信 --- 信号量_第2张图片

信号量的操作

在使用信号量之前,应用程序必须调用函数 OSSemCreate( ) 来创建一个信号量,OSSemCreate( ) 的原型为:

OS_EVENT  *OSSemCreate (
INT16U cnt      //信号量计数器初值
);

函数的返回值为已创建的信号量的指针。

μC/OS-II 任务的同步与通信 --- 信号量_第3张图片

请求信号量

任务通过调用函数 OSSemPend( ) 请求信号量,函数 OSSemPend( ) 的原型如下:

void  OSSemPend ( OS_EVENT *pevent, //信号量的指针
INT16U timeout, //等待时限
INT8U *err //错误信息
);      

参数 pevent 是被请求信号量的指针。
为防止任务因得不到信号量而处于长期的等待状态,函数 OSSemPend 允许用参数 timeout 设置一个等待时间的限制,当任务等待的时间超过 timeout 时可以结束等待状态而进入就绪状态。如果参数 timeout 被设置为 0,则表明任务的等待时间为无限长。

释放信号量

任务获得信号量,并在访问共享资源结束以后,必须要释放信号量,释放信号量也叫做发送信号量,发送信号量需调用函数 OSSemPost ( )。OSSemPost ( ) 函数在对信号量的计数器操作之前,首先要检查是否还有等待该信号量的任务。如果没有,就把信号量计数器 OSEventCnt加一;如果有,则调用调度器 OS_Sched( ) 去运行等待任务中优先级别最高的任务。
函数 OSSemPost ( ) 的原型为:

INT8U  OSSemPost    (
OS_EVENT *pevent    //信号量的指针
);

调用函数成功后,函数返回值为 OS_ON_ERR,否则会根据具体错误返回 OS_ERR_EVENT_TYPE、OS_SEM_OVF。

删除信号量

应用程序如果不需要某个信号量了,那么可以调用函数 OSSemDel( ) 来删除该信号量,这个函数的原型为:

OS_EVENT  *OSSemDel (
OS_EVENT *pevent,   //信号量的指针
INT8U opt,          //删除条件选项
INT8U *err          //错误信息
); 

互斥型信号量

在描述互斥型信号量的事件控制块中,除了成员 OSEventType 要赋以常数 OS_EVENT_TYPE_MUTEX 以表明这是一个互斥型信号量和仍然没有使用成员 OSEventPtr 之外,成员 OSEventCnt 被分成了低8位和高8位两部分:低8位用来存放信号值(该值为 0xFF 时,信号为有效,否则信号为无效),高8位用来存放为了避免出现优先级反转现象而要提升的优先级别 prio。

μC/OS-II 任务的同步与通信 --- 信号量_第4张图片

创建互斥信号量

创建互斥型信号量需要调用函数 OSMutexCreate( )。函数 OSMutexCreate( ) 的原型如下:

OS_EVENT  *OSMutexCreate    (
INT8U prio, //优先级别
INT8U *err  //错误信息
);

函数 OSMutexCreate( ) 从空事件控制块链表获取一个事件控制块,把成员 OSEventType 赋以常数 OS_EVENT_TYPE_MUTEX 以表明这是一个互斥型信号量,然后再把成员 OSEventCnt 的高8位赋以 prio(欲提升的优先级别),低8位赋以常数 OS_MUTEX_AVAILABLE(该常数值为 0xFFFF)的低8位(0xFF)以表明信号量尚未被任何任务所占用,处于有效状态。

请求互斥信号量

当任务需要访问一个独占式共享资源时,就要调用函数 OSMutexPend( ) 来请求管理这个资源的互斥型信号量,如果信号量有信号(OSEventCnt 的低8位为 0xFF),则意味着目前尚无任务占用资源,于是任务可以继续运行并对该资源进行访问,否则就进入等待状态,直至占用这个资源的其他任务释放了该信号量。
函数 OSMutexPend( ) 的原型为:

void  OSMutexPend   (
OS_EVENT *pevent,         //互斥型信号量指针
INT16U timeout,           //等待时限
INT8U *err                //错误信息
);
释放互斥信号量

任务可以通过调用函数 OSMutexPost( ) 发送一个互斥型信号量,这个函数的原型为:

INT8U  OSMutexPost (
OS_EVENT *pevent    //互斥型信号量指针
);

参考自:《μC/OS-II 入门教程》

转载于:https://www.cnblogs.com/GyForever1004/p/8721536.html

你可能感兴趣的:(数据结构与算法,内存管理,操作系统)