信号量是VxWorks中任务间通信的一种基本手段。VxWorks提供了三种信号量:二进制信号量、互斥信号量和计数信号量。下面针对实验中的程序编写和调试,讲一讲我对信号量的理解。
首先,信号量的使用必须包括semLib.h这个头文件。下面分类进行简单说明:
1.二进制信号量
   创建二进制信号量的函数:
SEM_ID semBCreate( int options , SEM_B_STATE initalState)
   该函数执行后,返回一个二进制信号量的ID。
   函数参数说明:
   options可以为SEM_Q_PRIORITY(值为0x1)和SEM_Q_FIFO(值为0x0)。这个选项是设定使用信号量的任务对信号量的抢占方式,当信号量可用时若有多个任务在等待,前者设定优先级高的任务先抢占,后者则设定处在等待队列前面的任务先抢占。
    initalState可以为SEM_FULL(值为1)和SEM_EMPTY(值为0)。前者初始化信号量为满(可用),后者初始化信号量为空(不可用)。
我对二进制信号量的使用的一些理解:
a.二进制信号信号量一般用在对那些独占性资源的分配中,take和give在同一个任务当中。比如有两个任务都需要用到打印机资源,则需要看信号量的option情况,如下定义A、B两个任务,都需要使用同一个打印机:  
TaskA  
{
        Take 信号量 
        打印
        Give信号量
    }  
TaskB  
{  
        Take信号量 
        打印  
        Give信号量
}  


b.二进制信号量用在同步的时候,一般take和give在不同的任务当中。如下面的A、B两个任务,对打印这个动作进行同步,防止没有纸而打印的情况:
TaskA  
{
        准备打印纸 
        Give信号量
    }  
TaskB  
{   
        Take信号量 
        开始打印  
}
2.互斥信号量
互斥信号量的创建函数:
SEM_ID semMCreate(int optipns)
该函数执行后,返回一个互斥信号量的ID。
options参数说明:
SEM_Q_PRIORITY(值为0x1):需要获取该信号量的任务基于优先级顺序排列。
SEM_Q_FIFO(值为0x0):需要获取该信号量的任务基于等待任务队列的先进先出顺序排列。
SEM_DELETE_SAFE(值为0x4):保护任务防止意外删除,当执行semTake操作时默认为taskSafe,当执行semGive的时候默认为taskUnsafe。
SEM_INVERSION_SAFE(值为0x8):保护系统,防止出现优先级倒置现象。
SEM_EVENTSEND_ERR_NOTIFY(值为0x10):任务发送事件失败时,会发送错误信号。
一般来说,互斥信号量主要用于保护临界资源, take和give成对出现。比如下面的A、B两个任务都要对数据库中的同一项进行操作,用来避免数据库在被操作时被其它任务所改写:  
TaskA  
{         
       Take信号量  
       写数据库  
       Give信号量
}  
TaskB  

       Take信号量  
       写数据库   
       Give信号量  
 }
   可以看出,互斥信号量和二进制信号量都能实现任务互斥访问临界资源的问题,但是这两者还是有一些不同的。下面谈谈互斥信号量与二进制信号量相比的优点:互斥信号量可以解决在互斥操作用二进制信号量时所引起的问题。这些问题包括资源拥有者的删除保护,以及由资源竞争引起的优先级逆转。先说资源的删除保护。互斥引起的一个问题会涉及到任务删除,即在由信号量保护的临界区中,需要防止执行任务被意外地删除。删除一个在临界区执行的任务是灾难性的,资源会被破坏,保护资源的信号量会变为不可获得,从而该资源不可被访问。通常删除保护是与互斥操作共同提供的。由于这个原因,互斥信号量通常提供选项来隐含地提供前面提到的任务删除保护的机制(即SEM_DELETE_SAFE这个参数选项)。
   再说说优先级逆转。优先级逆转发生在一个高优先级的任务被强制等待一段不确定的时间以便一个较低优先级的任务完成执行。考虑下面的假设:T1,T2和T3分别是高、中、低优先级的任务。T3通过拥有信号量而获得相关的资源。当T1抢占T3,为竞争使用该资源而请求相同的信号量的时候,它被阻塞。如果我们假设T1仅被阻塞到T3使用完该资源为止,情况并不是很糟。毕竟资源是不可被抢占的。然而,低优先级的任务并不能避免被中优先级的任务抢占,一个抢占的任务如T2将阻止T3完成对资源的操作。这种情况可能会持续阻塞T1等待一段不可确定的时间。这种情况就叫做优先级逆转,因为尽管系统是基于优先级的调度,但却使一个高优先级的任务等待一个低优先级的任务完成执行。
   最后谈谈优先级继承。互斥信号量有一个选项允许实现优先级继承的算法(即SEM_INVERSION_SAFE这个参数选项)。优先级继承通过在T1被阻塞期间提升T3的优先级到T1解决了优先级逆转引起的问题。这防止了T3,间接地防止T1,被T2抢占。通俗地说,优先级继承协议使一个拥有资源的任务以等待该资源的任务中优先级最高的任务的优先级执行。当执行完成,任务释放该资源并返回到它正常的或标准的优先级。因此,继承优先级的任务避免了被任何中间优先级的任务抢占。
3.计数信号量
计数信号量的创建函数:
SEM_ID semCCreate(int options, int initialCount)
该函数执行后,返回一个计数信号量的ID。
options参数说明:
SEM_Q_PRIORITY(值为0x1):需要获取该信号量的任务基于优先级顺序排列。
SEM_Q_FIFO(值为0x0):需要获取该信号量的任务基于等待任务队列的先进先出顺序排列。
SEM_EVENTSEND_ERR_NOTIFY(值为0x10):该参数可激活由于semGive失败而返回ERROR值。
initialCount参数为初始化计数信号量的值。由此可见,计数信号量的值是上不封顶的,只要能不断的semGive,信号量就可以增加。因此在使用计数信号量的时候必须注意信号量的值的边界问题,就是要根据实际资源的大小来确定。
    计数信号量是任务同步和互斥的另一种实现方式,其用法和二进制信号量类似,只是它可以保持信号量被释放的次数,主要用于保护一个资源的多个例程。
 
总结:信号量的使用是操作系统中一个非常重要的工具,几乎所有的操作系统都支持信号量的操作。信号量在资源的分配以及防止死锁方面具有不可替代的作用。