信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。(1)信号量的值为正的时候,说明它空闲。所测试的线程可以锁定而使用它。(2)若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒。
二.信号量的分类
在学习信号量之前,我们必须先知道——Linux提供两种信号量:
(1)内核信号量,由内核控制路径使用(驱动开发中使用)
(2)用户态进程使用的信号量,这种信号量又分为POSIX信号量和SYSTEM V信号量。
POSIX信号量又分为有名信号量和无名信号量。
(1)有名信号量,其值保存在文件中, 所以它可以用于线程也可以用于进程间的同步。(2)无名信号量,其值保存在内存中。
三、POSIX 信号量与SYSTEM V信号量的比较
1. 对POSIX来说,信号量是个非负整数。常用于线程间同步。
而SYSTEM V信号量则是一个或多个信号量的集合,它对应的是一个信号量结构体,这个结构体是为SYSTEM V IPC服务的,信号量只不过是它的一部分。常用于进程间同步。
2.POSIX信号量的引用头文件是“<semaphore.h>”,而SYSTEM V信号量的引用头文件是“<sys/sem.h>”,它通常依赖于另两个头文件:
- #include <sys/types.h>
- #include <sys/ipc.h>
3.从使用的角度,System V信号量是复杂的,而Posix信号量是简单。比如,POSIX信号量的创建和初始化或PV操作就很非常方便。
四、System V 信号量函数定义如下:
1、创建信号量集合come from /usr/include/sys/sem.h
函数原型:int semget(key_t key,int nsems,int semflg);
参数解释:
key:所创建或打开信号量集的键值。需要是唯一的非零整数。
nsems:创建的信号量集中的信号量的个数,该参数只在创建信号量集时有效。几乎总是取值为1.
flag:调用函数的操作类型,也可用于设置信号量集的访问权限,两者通过or表示
2、控制信号量集合、信号量come from /usr/include/sys/sem.h
函数原型:int semctl(int semid,int semnum,int cmd,union semun)
参数解释:
sem_id是由semget返回的信号量标识符。
sem_num:表示集合中信号量的编号。
cmd:表示对信号量的操作。(1)对信号量集的操作(2)对单个信号量的操作
semun联合结构的定义:
- semun是在linux/sem.h中定义的:
- /*arg for semctl systemcalls.*/
- union semun{
- int val;/*value for SETVAL*/
- struct semid_ds *buf;/*buffer for IPC_STAT&IPC_SET*/
- ushort *array;/*array for GETALL&SETALL*/
- struct seminfo *__buf;/*buffer for IPC_INFO*/
- void *__pad;
3、 信号量操作 c ome from /usr/include/sys/sem.h
函数原型:int semop( int semid, struct sembuf semoparray[], size_t nops )
参数解释:
参数semid:是一个通过semget函数返回的一个信号量标识符
参数nops:标明了参数semoparray所指向数组中的元素个数
参数semoparray:是一个struct sembuf结构类型的数组指针,结构sembuf:来说明所要执行的操作,其定义如下:
- struct sembuf{
- unsigned short sem_num;
- short sem_op;
- short sem_flg;
- }
sem_num是操作的信号量编号
sem_op是信号量的操作,值是一个整数,通常只会用到两个值:1----P操作,-1---V操作。
sem_flg说明函数semop的行为。通常被设置为SEM_UNDO。
五、实例程序
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> static int semaphore_p(void); static int semaphore_v(void); static int set_semvalue(void); static int get_semvalue(void); union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ unsigned short int *array; /* array for GETALL, SETALL */ struct seminfo *__buf; /* buffer for IPC_INFO */ }; int sem_id; int main(int argc,char *argv) { pid_t pid; int i; int value; key_t key; int status; if((pid=fork())==-1) { perror("fork"); exit(EXIT_FAILURE); } else if(pid==0) { if((sem_id=semget((key_t)123456,1,IPC_CREAT|0770))==-1) { perror("semget"); exit(EXIT_FAILURE); } if (!set_semvalue()) { fprintf(stderr, "Failed to initialize semaphore\n"); exit(EXIT_FAILURE); } value=get_semvalue(); printf("this is child,the current value is %d\n",value); if(!semaphore_v()) { fprintf(stderr, "Failed to v operator\n"); exit(EXIT_FAILURE); } value=get_semvalue(); printf("the child %d V operator,value=%d\n",i,value); printf("child exit success\n"); exit(EXIT_SUCCESS); } else //parent { sleep(3); if((sem_id=semget((key_t)123456,1,IPC_CREAT|0770))==-1) { perror("semget"); exit(EXIT_FAILURE); } value=get_semvalue(); printf("this is parent ,the current value is %d\n",value); printf("the parent will remove the sem\n"); if(semctl(sem_id,0, IPC_RMID,(struct msquid_ds*)0)==-1) { perror("semctl"); exit(EXIT_FAILURE); } return 0; } } static int set_semvalue(void) { union semun sem_union; int value; sem_union.val = 5; if (semctl(sem_id, 0, SETVAL, sem_union) == -1) return(0); printf("set value success,"); printf("init value is %d\n",get_semvalue()); return(1); } static int get_semvalue(void) { int res; if((res=semctl(sem_id, 0, GETVAL)) == -1) { perror("semctl"); exit(EXIT_FAILURE); } return res; } static int semaphore_v(void) { struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = 1; /* V() */ //sem_b.sem_flg = SEM_UNDO; sem_b.sem_flg=0; if (semop(sem_id, &sem_b, 1) == -1) { perror("semop"); return(0); } return(1); }