信号量:
信号量是用来控制临界资源的,那什么是临界资源呢:
在系统中有许多进程,他们共享各种资源,然而又很多资源一次只能供一个进程来使用,一次仅允许一个进程使用的资源称为临界资源,比如共享内存。共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取,所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量。
信号量集:
就是多个信号量的集合
P操作:
类似于拿锁,通过P操作来获取访问临界资源的权限。运行P,信号量S的值将被减少。企图进入临界区块的进程,需要先运行 P(wait())。当信号量S减为负值时,进程会被挡住,不能继续;当信号量S不为负值时,进程可以获准进入临界区块。
V操作:
类似于放锁,P操作后其他进程不能访问这个临界资源,只有P操作的进程执行了V操作后其他进程才可以访问。运行 V,信号量S的值会被增加。结束离开临界区块的进程,将会运行 V(又称signal())。当信号量S不为负值时,先前被挡住的其他进程,将可获准进入临界区块。
一、相关API
1.创建或者打开信号量集
int semget(key_t key, int nsems, int semflg);
//获取、创建一个信号量集,成功返回ID号,失败返回-1
//nsems为你想创建的信号量集中信号量的个数,semflg为权限,通常可设置为IPC_CREAT|0777。
2.控制信号量
int semctl(int semid, int semnum, int cmd, ...);
//控制、初始化信号量
//semid为信号量组的ID,semnum为你要控制信号量组的第几个信号量(0开始),cmd为宏
union semun {
int val; //开始有几把锁**
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
如果cmd指定为SETVAL(具体见man手册),则需要第四个参数,第四个参数为联合体
3.P操作或者V操作
int semop(int semid, struct sembuf *sops, size_t nsops);
//sops是一个指针,指向一个数组。nsops是数组的个数
struct sembuf sops[2];
sops[0].set_num = 0;//信号量的编号,可以看semctl第二个参数为多少
sops[0].set_op = -1;//1为操作后锁+1,-1为操作后锁-1
sops[0].set_flg= SEM_UNDO;//可以设置为SEM_UNDO,意思是当进程终止时取消对锁操作
--------------------------------------------------------------------------
sops[1].set_num = 0;
sops[1].set_op = 1;
sops[1].set_flg= SEM_UNDO;
if(semop(semid,sops,2) == -1){
exit(-1);
}
例子:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
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;
};
void pkey(int id)
{
struct sembuf sops;
sops.sem_num = 0;
sops.sem_op = -1;
sops.sem_flg = SEM_UNDO;
semop(id,&sops,1);
}
void vkey(int id)
{
struct sembuf sops;
sops.sem_num = 0;
sops.sem_op = 1;
sops.sem_flg = SEM_UNDO;
semop(id,&sops,1);
}
int main()
{
key_t key;
key = ftok(".",5);
int semId = semget(key,1,IPC_CREAT|0777);
union semun initsem;
initsem.val = 0;
semctl(semId,0,SETVAL,initsem);
int pid = fork();
if(pid >0){
pkey(semId);
printf("this is father\n");
vkey(semId);
}
else if(pid == 0){
printf("this is child\n");
vkey(semId);
}else{
printf("fork error\n");
}
return 0;
}