信号量是一个计数器,用于为多个进程提供对共享数据对象的访问。  
在信号量上只有三种操作可以进行,初始化、递增和增加,这三种操作都是原子操作。递减操作可以用于阻塞一个进程,增加操作用于解除阻塞一个进程。  
为了获得共享资源,需要测试信号量,若信号量为正,则进程可以使用该资源,这时信号量值减一。否则信号量值为0,进程进入休眠状态。当进程不再使用由一个信号量控制的共享资源时,信号量值加一。如果有正在休眠的进程,则唤醒它们。常用的信号量的形式为二元信号量。即是原子性的。  
信号量的功能 :负责数据操作的互斥、同步等功能。本质上是一种数据操作锁。我们为什么要使用信号量呢?为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使⽤用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来协调进程对共享资源的访问的。其中共享内存的使用就要用到信号量。  
信号量只能进行两种操作等待和发送信号,PV操作 ,即P(sv)和V(sv),P申请资源,则将可用资源数-1,V释放资源,则将可用资源数+1。  
内核为每个信号量集合维护着一个semid_ds结构:  
struct  semid_ds
{struct  ipc_perm sem_perm;unsigned  short  sem_nsems; time_t sem_otime;time_t sem_ctime;
};每个信号量都有一个无名的结构:  
  unsigned  short   semval;   unsigned  short   semzcnt;  unsigned  short   semncnt;  pid_t           sempid;   
当我们想使用信号量时,首先要通过调用函数semget来获得一个信号量ID:  
#include<sys/sem.h> 
int  semget(key_t key,int  nsems,int  flag);
semctl函数包含了多种信号量操作  
int  semctl(int  semid,int  semnum,int  cmd,...);
union  semun
{int  val;       struct  semid_ds *buf;     unsigned  short  *array ;    
};我们通常使用的:IPC_RMID:从系统中删除该信号量集合  
SETVAL:设置成员semnum的semval值,该值由arg.val指定  
函数semop自动执行信号量集合上的操作数组。  
int  semop(int  semid,struct  sembuf semoparray[],size_t nops);
struct  sembuf
{unsigned  short  sem_num;   short  sem_op;  short  sem_flg;  
};
对集合中每个成员的操作由相应的sem_op值规定。此值可以为负值,0,正值。最易于处理的是为正值,说明需要释放资源,则sem_op的值会加到信号量值上,如果指定了undo标志,则从此信号量调整之后的值上减去sem_op;如果sem_op是负值,说明需要申请资源,则信号量会减去sem_op的绝对值,如果指定undo标志,则sem_op的绝对值也加到信号量的调整值上。sem_op为0,表示调用进程希望等待到该信号量值为0。  
对于信号量调整,如果在进程终止时,它占用了经由信号量分配的资源,那么就会成为一个问题。无论何时只要为信号量操作指定了SEM_UNDO标志,然后分配资源(sem_op< 0),那么内核就会记住该特定信号量,分配给调用进程多少资源。对每个操作都指定SEM_UNDO,以处理在未释放资源条件下进程终止的情况。  
信号量主要解决互斥与同步问题,下面举个栗子:(实现父子进程输出成对AA或BB)  
#ifndef _COMM_H_ 
#define _COMM_H_ #include<stdio.h> 
#include<sys/types.h> 
#include<sys/ipc.h> 
#include<sys/sem.h> #define PATHNAME "." 
#define PROJ_ID 0X6666 
union semun {int   val;    struct  semid_ds *buf;    unsigned short   *array;  struct  seminfo  *__buf;  };int  CreateSem(int  nums);
int   GetSem(int  nums);
int  DestroySem(int  semid);
int  initSem(int  semid,int  nums,int  initval);
int  SemP(int  semid,int  who);
int  SemV(int  semid,int  who);#endif  //_COMM_H_ 
static int  CommSemSet(int  nsems,int  flags)
{key_t key = ftok(PATHNAME,PROJ_ID);if (key < 0 ){//printf ("%d \n" , key);perror("ftok" );return  -1 ;}int  semid = semget (key,nsems,flags);if (semid < 0 ){perror("semget" );return  -2 ;}return  semid;
}int   CreateSemSet(int  nums)
{return  CommSemSet(nums,IPC_CREAT|IPC_EXCL|0666 );
}int  GetSem(int  nums)
{return  CommSemSet(nums,IPC_CREAT);
}
int  DestroySem(int  semid)
{if (semctl (semid,0 ,IPC_RMID) < 0 ){perror("semctl" );return  -1 ;}return  0 ;
}int  initSem(int  semid,int  nums,int  initval)
{union semun _un;_un.val =  initval;if (semctl (semid,nums,SETVAL,_un) < 0 ){perror("semctl" );return  -1 ;}return  0 ;
}static int  CommPV(int  semid,int  who,int  op)
{struct sembuf _sem;_sem.sem_num = who;_sem.sem_op = op;_sem.sem_flg = 0 ;if (semop (semid,&_sem,1 ) < 0 ){perror("semop" );return  -1 ;}   return  0 ;
}
int  SemP(int  semid,int  who)
{return  CommPV(semid,who,-1 );
}
int  SemV(int  semid,int  who)
{return  CommPV(semid,who,1 );
}
#include"comm.h" int  main()
{int  semid = CreateSemSet(1 );initSem(semid,0 ,1 ); pid_t id = fork();if (id == 0 ){int  _semid = GetSem(0 );while (1 ){SemP(_semid,0 );printf ("A" );fflush(stdout);usleep(123456 );printf ("A" );fflush(stdout);usleep(345678 ); SemV(_semid,0 );}}else {while (1 ){SemP(semid,0 );printf ("B" );fflush(stdout);usleep(234567 );printf ("B" );fflush(stdout);usleep(456789 );SemV(semid,0 );}wait(NULL);}DestroySem(semid);printf ("sem quit!\n" );return  0 ;
}//Makefile
sem: sem.c  comm.c gcc -o $@ $^
.PHONY: clean
clean: rm -f sem
运行结果: