一、什么是信号量 信号量: 信号量的本质是一种数据操作锁,它本身不具有数据数据交换的功能,而是通过控制其他的通信资源(文件、外部设备)来实现进程间通信,它本身只是一种外部资源的标识。信号量在此过程中负责数据操作的互斥、同步功能。 信号量就相当于是一个计数器,当有进程对它所管理的资源进行请求时,进程先要读取信号量的值,大于0资源可以请求,等于0,资源不可以用,这时进程会进入睡眠状态直至资源可用。当一个进程不再使用资源时,信号量+1(称为V操作),反之当有进程使用资源时,信号量-1(称为P操作)。对信号量的值操作均为原子操作。 信号量的特点: 1、信号量和消息队列一样也是在内核中实现的,信号量的生命周期是随内核的。 2、信号量并非是单个的非负的值,必须定义为一个含有一个或多个信号量值的集合。当创建信号量的时候就要指定集合当中信号量值的数量。 3、信号量的创建和初始化不是原子的,所以就会导致出一个问题。由于不互斥,可能会出现创建后未初始化就被另外一个进程获取。 二、为什么要使用信号量 为了防止多个进程在访问共享资源为引发的问题。信号量可以协调进程对共享资源的访问,也就是用来保护临界资源的。任一时刻只能有一个执行线程进入临界区。 临界区: 访问临界资源的代码片段。 临界资源: 一次只允许一个进程使用的资源。 一个进程要获得共享资源,需要执行的操作为: 1、测试控制该进程的信号量。 2、若信号量为正,该进程可以使用该资源。这个时候信号量的值减1,表示进程正在使用一个资源。 3、若信号量为0,这个时候进入休眠状态,直到信号量大于0才可以获得资源。 三、信号量的操作函数 1、创建/获取一个信号量集合
int semget(key_t key,int nsems,int semflg);
返回值:成功就返回信号量的集合semid,失败就返回-1。 key:可以用key_t ftok(const char* pthname,int proj_id)来获取。 System V IPC使用此类型作为系统对它们的唯一标识(名字)。实就为int类型。通常使用ftok函数类获取key值。ftok把一个已经存在的路径名和一个整数标识转换成一个key_t值。 nsems:这个参数表示你要创建的信号量集合中的信号量的个数。信号量只能以集合的形式创建。 semflg:同时使用IPC_CREAT和IPC_EXCL则会创建一个新的信号量集合。若已经存在的话则返回-1。单独使用IPC_CREAT的话会返回一个新的或者已经存在的信号量集合。 2、信号量结合的操作
int semop(int semid,struct sembuf *sops,unsigned nsops); int semtimedop(int semid, struct sembuf *sops, unsigned nsops, struct timespec *timeout);返回值:成功返回的是0,失败返回的是-1 semid:信号量集合的id
struct sembuf *sops: struct sembuf { unsigned short sem_num; /* semaphore number */ short sem_op; /* semaphore operation */ short sem_flg; /* operation flags */ };
参数含义: sem_num:信号量是以集合的形式存在的,相当所有信号量在一个数组里边,sem_num表示信号量在集合中的编号。 sem_op:表示该信号量的操作(P操作还是V操作)。如果其值为正数,该值会加到现有的信号内含值中。通常用于释放所控资源的使用权;如果sem_op的值为负数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值。通常用于获取资源的使用权 sem_flg:信号操作标志,它的取值有两种。IPC_NOWAIT和SEM_UNDO。 IPC_NOWAIT:对信号量的操作不能满足时,semop()不会阻塞,而是立即返回,同时设定错误信息。 SEM_UNDO:程序结束时(不管是正常还是不正常),保证信号值会被设定semop()调用之前的值。这样做的目的在于避免程序在异常的情况下结束未将锁定的资源解锁(死锁),造成资源永远锁定。 nsops:表示要操作信号量的个数。因为信号量是以集合的形式存在,所以第二个参数可以传一个数组,同时对一个集合中的多个信号量进行操作。 3、信号量的销毁
int semctl(int semid,int semnum,int cmd,…)
信号量的销毁,semctl在semid标识的信号量集合上,或者在该信号量的集合上的第semnumm个信号量上执行cmd指定的控制命令。cmd的不同这个函数有三个或者四个参数,当有第四个参数的时候这个参数的类型是union。
union semun{ int val; //使用的值 struct semid_ds *buf; //IPC_STAT、IPC_SET使用缓存区 unsigned short *array; //GETALL、SETALL使用的缓存区 struct seminfo *__buf; //IPC_INFO(linux特有)使用缓存区 };返回值:失败返回-1。成功返回0。 semid:信号量集合的编号。 semnum:信号量在集合中的标号。
四、信号量的代码展示 一个简单的信号量的操作: 头文件:
comm.h #ifndef __COMM_H__ #define __COMM_H__ #include<stdio.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/sem.h> #define PATHNAME "." #define PROJID 0x6666 int Init_Sem(int semid); int Get_Sem(); int Creat_Sem(); int Destory_Sem(int semid); int P_Sem(int semid,int which); int V_Sem(int semid,int which); #endif //!__COMM_H__comm.c:
comm.c #include"comm.h" int Init_Sem(int semid) { union semun { int val; /* Value for SETVAL */ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ unsigned short *array; /* Array for GETALL, SETALL */ struct seminfo *__buf; /* Buffer for IPC_*/ }; union semun semun; semun.val=1; int ret=semctl(semid,0,SETVAL,semun); if(ret<0) { perror("semctl"); return -1; } return ret; } static int Comm_Sem(int flags,int nsems) { key_t semkey=ftok(PATHNAME,PROJID); if(semkey<0) { perror("ftok"); return -1; } int semid=semget(semkey,nsems,flags); if(semid<0) { perror("semget"); return -2; } return semid; } int Creat_Sem() { return Comm_Sem(IPC_CREAT|IPC_EXCL|0666,1); } int Get_Sem() { return Comm_Sem(IPC_CREAT,0); } int Destory_Sem(int semid) { int ret=semctl(semid,0,IPC_RMID); } static int Comm_Op(int semid,int which,int op) { struct sembuf sembuf; sembuf.sem_num=which; sembuf.sem_op=op; sembuf.sem_flg=0; int ret=semop(semid,&sembuf,1); if(ret<0) { perror("semop"); return -1; } return ret; } int P_Sem(int semid,int which) { return Comm_Op(semid,which,-1); } int V_Sem(int semid,int which) { return Comm_Op(semid,which,1); }sem.c:
sem.c #include<unistd.h> #include"comm.h" int main() { int semid=Creat_Sem(); int ret=Init_Sem(semid); if(ret<0) { return -1; } printf("semid:%d\n",semid); pid_t id=fork(); if(id==0) { //child while(1) { P_Sem(semid,0); usleep(10002); printf("A"); fflush(stdout); usleep(1080); printf("A"); fflush(stdout); V_Sem(semid,0); } } else { while(1) { P_Sem(semid,0); printf("B"); usleep(10080); fflush(stdout); printf("B"); usleep(12000); fflush(stdout); V_Sem(semid,0); } wait(NULL); } Destory_Sem(semid); return 0; }