管道,消息队列,共享内存,信号都涉及数据,可以进行数据交互;
信号量(钥匙):不涉及数据交互,只管理临界资源 ,用于进程间的互斥与同步; 临界资源(房间):一次只能允许一个进程使用的资源,需要信号量来管控,如果不去管控则会损坏;
P操作(通过):拿锁 V操作(释放):放回锁 信号量集:多个信号量的一个组合;
函数原型
1 #include <sys/sem.h>
2 // 创建或获取一个信号量组:若成功返回信号量集ID,失败返回-1
3 int semget(key_t key, int nsems, int semflg);
4 // 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-1
5 int semop(int semid, struct sembuf semoparray[], size_t numops);
6 // 控制信号量的相关信息
7 int semctl(int semid, int sem_num, int cmd, ...);
semget函数(创建/获取):
#include <sys/shm.h>
int semget( key_t key, int nsems, int flag);
key:信号标识符 nsens:信号量集中信号量的个数,一般设置1个 flag:指定操作权限,一般设置IPC_CREAT|0666
semctl函数(初始化信号量):
int semctl(int semid, int semnum, int cmd, ...);
semid:是semget函数成功返回的ID; semunm:信号量集的下标,指定对哪个信号量进行操作,一般只有一个信号量,对第一个信号量进行操作就设置成0; cmd(宏):指定操作类型,一般设置这IPC_RMID, SETVAL等操作 SETVAL:用来把信号量初始化为一个已知的值
IPC_RMID:用于删除一个已经无需继续使用的信号量标识符
配置semctl函数里面的第4个参数:涉及到一个用户自定义的联合体
union semun {
int val; /* Value for SETVAL */
只定义val这个,原先有没有锁,有为1,没有为0
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
semop函数(取/放钥匙函数):
int semop(int semid, struct sembuf *sops, unsigned nsops);
semid:是semget函数成功返回的ID *sops: 结构体数组指针
struct sembuf{
short sem_num; // 除非使用一组信号量,否则它为0
short sem_op; // 信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,
// 一个是+1,即V(发送信号)操作。
short sem_flg; // 通常为SEM_UNDO,使操作系统跟踪信号,
// 并在进程没有释放该信号量而终止时,操作系统释放信号量
};
nops:操作*sops结构体数组的个数,只有一个,就不需要定义数组;
函数例子:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
//int semget(key_t key, int nsems, int semflg);
//int semctl(int semid, int semnum, int cmd, ...);
//int semop(int semid, struct sembuf *sops, unsigned nsops);
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_INFO
(Linux-specific) */
};
void pGetKey(int id)
{
//struct sembuf sops[2];
struct sembuf sops;
sops.sem_num = 0;
sops.sem_op = -1; //钥匙减一
sops.sem_flg = SEM_UNDO;
semop(id,&sops, 1);//1表示只有一个
printf("pGetKey\n");
}
void vPutKey(int id)
{
//struct sembuf sops[2];
struct sembuf sops;
sops.sem_num = 0;
sops.sem_op = 1; //钥匙加一
sops.sem_flg = SEM_UNDO;
semop(id,&sops, 1);//1表示只有一个
printf("vPutKey\n");
}
int main()
{
key_t key;
key = ftok(".",2);
int semid;
semid = semget(key,1,IPC_CREAT | 0666);//1表示信号量集有一个信号量,创建信号量
union semun initsem;
//initsem.var = 1; //表示只有一把钥匙
initsem.val = 0; //表示没有钥匙了
semctl(semid,0,SETVAL,initsem);//信号量初始化,0表示操作第0个信号量,
//SETVAL用来设置信号量的值设置为initsem
int pid = fork();
if(pid > 0){
pGetKey(semid);//拿钥匙锁
printf("this is father\n");
vPutKey(semid);//放钥匙锁
semctl(semid,0,IPC_RMID); //删除锁
}
else if(pid == 0){
printf("this is child\n");
vPutKey(semid); //放钥匙,原先没有钥匙,父进程运行时会阻塞,
//只有当子进程将钥匙放进去父进程才会运行
}
else{
printf("fork error\n");
}
return 0;
}
运行的结果是原先没有锁,子进程先运行,放回一把锁,父进程拿到一把锁,开始运行父进程,然后父进程吧锁放回去,最后删除锁;