zoukankan      html  css  js  c++  java
  • μ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;
    

    把一个任务置于等待状态要调用 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 作为链接指针,把这些空事件控制块链接成一个单向链表。由于链表中的所有控制块尚未与具体事件相关联,故该链表叫做空事件控制块链表。

    信号量的操作

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

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

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

    请求信号量

    任务通过调用函数 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。

    创建互斥信号量

    创建互斥型信号量需要调用函数 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 入门教程》

  • 相关阅读:
    go2基本类型
    go1
    android studio 使用
    ios34---GDC,dispatch_once
    ios33--线程通信
    ios33--线程安全
    ios32---线程的状态
    ios31--NSThread
    ios30---pthread, NSThread, GCD, NSOperation
    ios29--多线程
  • 原文地址:https://www.cnblogs.com/GyForever1004/p/8721536.html
Copyright © 2011-2022 走看看