信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。(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);
}