一、信号量的概念
信号量(又名信号灯):与其他进程间通信方式不大相同,它主要提供对进程间共享资源访问控制机制,主要用途是保护临界资源,相当于内存中的标志,进程可以根据它判断是否能够访问某些共享资源,同时,进程也可以修改标志,除了用于访问控制外,还可用于进程同步,信号量还可以保护进程间的同步互斥。关于同步和互斥概念如下:
同步:进程间按一定的顺序去执行
互斥:某一进程占用资源时,只有其释放资源后其他进程才能使用。
二、信号量的分类
信号量可分为这两类:
1、二值信号灯:信号灯的值只能取0或1,类似于互斥锁。 但两者有不同:
信号灯强调共享资源,只要共享资源可用,其他进程同样可以修改信号灯的值;
互斥锁更强调进程,占用资源的进程使用完资源后,必须由进程本身来解锁后才能被其他进程使用
2、计数信号灯:信号灯的值可以取任意非负值(受内核本身约束)
三、信号量的生命周期
信号量是随内核持续的,具有持续性,只有在内核重起或显示删除一个信号量时,这个信号量才会被真正的删除
四、键值
键值的概念:信号量的内核持续性要求每个信号量在系统范围内都有对应的唯一的键值,要获得一个信号量的标识符,必须提供该信号量的键值,这在后面创建信号量时将会提及。
键值的获取:对于键值的获取,我们通常通过ftok()函数来获得
函数原型:key_t ftok(char *pathname,char proj);
函数的作用:返回文件名对应的键值
参数:
pathname:文件名
Proj:项目名(不为0即可)
五、信号量的使用步骤:
对信号量的使用主要为以下步骤:
①使用semget函数创建信号量,semget函数相关如下:
函数的原型:int semget(key_t key,int nsems,int semflg);
作用:创建信号量
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
参数:
key:键值,可以设置为IPC_PRIVATE,也可以由ftok()获得
nsems:信号量的数目,通常设置为1
semflg:当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误
返回值:执行成功,返回信号量的标识符;执行失败,返回-1.
②初始化信号量,通过semctl函数实现
函数的原型:int semctl(int semid,int semnum,int cmd,union semun arg);
作用:对信号量进行控制,包括初始化和删除等
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
参数:
semid:信号量的标识符
semnum:信号量的编号,通常设置为0,表示第一个信号量
cmd:
IPC_STAT:获得信号量
IPC_SETVAL:设置信号量的值(信号数据结构中的val)结构如下:
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
};
IPC_GETVAL:获取信号量的值
IPC_RMID:删除信号量
返回值:根据cmd的不同返回值也不同:
①cmd为IPC_STAT、IPC_SETVAL、IPC_RMID且执行成功时返回值为0
②cmd为IPC_GETVAL且执行成功时返回值为信号量的值
③执行出错,返回-1
③进行PV操作:用semop()实现
函数的原型:int semop(int semid, struct sembuf *sops, size_t nsops);
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
作用:执行信号量的PV操作
参数:
semid:信号量的标识符
sops:指向sembuf结构的指针,结构如下:
struct sembuf{
short sem_num;//除非使用一组信号量,否则它为0
short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,
//一个是+1,即V(发送信号)操作。
short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,
//并在进程没有释放该信号量而终止时,操作系统释放信号量
};
nops:通常设置为1
返回值:执行成功,返回信号量的标识符;执行出错,返回-1
④ 删除信号量,用上面的semctl函数执行,参数选择IPC_RMID.
六、用信号量实现进程访问共享资源互斥
下面就以两个不相关的进程来说明进程间如何通过信号量来进行互斥。其中一个文件sem1.c创建信号量,并向test.txt文本中写入字符串“hello",另一个文件sem2.c访问共享资源test.txt时会被互斥,等待sem1.c写完后再向文本后添加入“world”字符串.
sem1.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int semid;
int fd;
int ret;
key_t key;
struct sembuf sops;
key = ftok("/root/201610/1023/sem/test.txt",1);获取键值
semid = semget(key,1,IPC_CREAT); //创建信号量
if(semid == -1)
{
printf("create sem error!\n");
exit(1);
}
ret = semctl(semid,0,SETVAL,1); //设置信号量的值
printf("the value of sem is %d\n",ret);
fd = open("/root/201610/1023/sem/test.txt",O_RDWR|O_CREAT|O_APPEND);
sops.sem_num = 0;
sops.sem_op = -1;
semop(semid,&sops,1);//p操作
write(fd,"hello",10);
sops.sem_num = 0;
sops.sem_op = +1;
semop(semid,&sops,1);//v操作
close(fd);
return 0;
}
sem2.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <fcntl.h>
int main()
{
int semid;
int fd;
key_t key;
struct sembuf sops;
key = ftok("/root/201610/1023/sem/test.txt",1);
fd = open("/root/201610/1023/sem/test.txt",O_RDWR|O_CREAT|O_APPEND);
sops.sem_num = 0;
sops.sem_op = -1;
sops.sem_num = 0;
sops.sem_op = -1;
semop(semid,&sops,1);//p操作
write(fd,"world",20);
sops.sem_num = 0;
sops.sem_op = +1;
semop(semid,&sops,1);//v操作
close(fd);
return 0;
}