1、信号量
信号量本质上是一个计数器(不设置全局变量是因为进程间是相互独立的,而这不一定能看到,看到也不能保证++引用计数为原子操作),它和管道不同,它不以传送数据为主要目的,它主要是用来保护共享资源(信号量也属于临界资源),使得资源在一个时刻只有一个进程独享。
临界资源:具有排他性的资源
临界区:访问临界资源的代码段
2、信号量的工作原理
由于信号量只能进行两种操作,等待和发送信号,即P, V操作:
P操作:上锁,如果计数器的值大于0,就减1;如果它的值为0,就挂起该进程的执行
V操作:解锁,如果有其他进程因等待而被挂起,就让它恢复运行,如果没有进程因等待而挂起,计算器加1.
在信号量进行PV操作时都是原子操作(因为它需要保护临界资源)
原子操作:单指令的操作称为原子的,单条指令的执行是不会被打断的。
3、进程同步:
进程同步也就是进程之间直接的制约关系,是为完成某种任务而建立的两个或多个线程,这个线程需要在某些位置上协调他们的工作次序而等待、传递信息所产生的制约关系。进程间的直接制约关系来源于他们之间的合作。
4、信号量常用函数:
原型:int semget(key_t key,int nsems,int flags)
作用:创建信号量
参数:key表示信号集的名字,一般由ftok函数产生。
nsems表示信号集中信号量的个数。
flags标识信号量集合的权限,如0644,和以下参数一起使用。
IPC_CREAT表示key不存在就创建
IPC_EXCL表示key存在就返回失败
IPC_NOWAIT表示如果需要等待,则直接返回错误。
返回值:成功返回一个非负整数即该信号量的标识码,失败返回-1;
原型:int semctl(int semid, int semnum, int cmd, ...);
作用:删除或初始化信号量值
参数:semid为semget返回的信号量标识
semnum为信号量集中的第几个信号量
cmd为要采取的操作:
一般使用以下两种:GETVAL获取信号量的值;SETVAL设置信号量的值。
第四个参数为联合体union semun,用来设置信号量的值
cmd操作为GETVAL时:semctl第二个参数为信号量集的编号,若执行成功,semctl返回当前信号量的值,失败返回-1;
cmd操作为SETVAL时,第四个参数要设置val,成功返回0,失败返回-1;
原型:int semop(int semid, struct sembuf *sops, unsigned nsops);
作用:修改集合中一个或多个信号量的值
参数:semid信号量标识
sops是一个sembuf结构体指针
nsops标识信号量个数
struct sembuf{
short sem_num; //除非使用一组信号量,否则它为0
short sem_op; //信号量在一次操作中需要改变的数据,通常是两个数,
//一个是-1,即P(等待)操作,
//一个是+1,即V(发送信号)操作。
short sem_flg; //通常为SEM_UNDO,使操作系统跟踪信号量,
//并在进程没有释放该信号量而终止时,操作系统释放信号量
};
#include <stdio.h>
#include <stdlib.h>
#include <sys//ipc.h>
#include <sys/sem.h>
union semun{
int val;
};
void P(int id)
{
//设置信号量的值,P操作时sem_op为-1
struct sembuf sb;
sb.sem_num = 0;
sb.sem_op = -1;
sb.sem_flg = 0;
semop(id, &sb, 1);
}
void V(int id)
{
//设置信号量的值,V操作时sem_op为1
struct sembuf sb;
sb.sem_num = 0;
sb.sem_op = 1;
sb.sem_flg = 0;
semop(id, &sb, 1);
}
int main()
{
//创建信号量
int id = semget(123,1,IPC_CREAT|0644);
if(id == -1)
{
perror("id");
exit(1);
}
printf("semget success!\n");
union semun su;
su.val = 1;
//初始化信号量
semctl(id, 0, SETVAL, su);
pid_t pid = fork();
if(pid == 0)
{
//获取信号量的标识
int semid = semget(123, 0, 0);
while(1)
{
//P操作,上锁
P(semid);
printf("A\n");
sleep(1);
printf("A\n");
sleep(1);
//V操作,解锁
V(semid);
}
}
else if(pid > 0)
{
int semid = semget(123, 0, 0);
while(1)
{
P(semid);
printf("B\n");
sleep(1);
printf("B\n");
sleep(1);
V(semid);
}
}
}
[root@livedvd tmp]# ./sem_test
semget success!
B
B
A
A
B
B
A
A
B
B
A
A
可以看到,打印结果时,AA,BB是成对出现的,如果没有加信号量,AB出现的顺序是乱序的。
参考:
https://blog.csdn.net/skyroben/article/details/72513985
https://blog.csdn.net/enjoymyselflzz/article/details/81603577
https://blog.csdn.net/l__xing/article/details/80217842