文章目录
前言:进程同步篇
前一节学习了管道完成进程间通信(IPC),其主要机制就是使用虚拟文件系统(VFS)的管道文件的半双工的通信。
为什么需要进行进程同步?进程异步不是效率更高吗?
因为牵扯到两个概念
两个概念:
- 临界资源:同一时刻,只允许一个进程或者线程访问的资源(打印机等)
- 临界区:访问临界资源的代码段(p和v操作之间的代码,尽量减少代码量,代码量过多会导致p操作阻塞,影响程序执行效率)
- 原子操作:不能被终止的,一旦开始操作,必须等待操作完成(申请临界资源的操作)
举个栗子(一定要看!!!)
一个试衣间,是不是在某个时刻只能被一个人使用呢?答案是肯定的,此时的一个试衣间就算是临界资源;那你在试衣间干什么?那废话,肯定试衣服了,此时的试衣服的过程就算是临界区,那为什么在试衣间换衣服的过程就是临界区呢?折磨说吧,你(进程a)进入试衣间正在换衣服,秃然之间,有个猥琐大汉(进程b)闯进了你的试衣间,那是不是完蛋了。所以你在使用试衣间(临界资源)之前,肯定要反锁(P操作),当你换衣完成,出来的时候要开锁(V操作),在你换衣服的整个过程中就是访问临界资源的操作(代码操作临界资源)
(一)信号量
信号量是一个特殊的变量,一般取正数值。代表着允许访问资源的数目。
- 获取资源时,需要对信号量原子减一,称为p操作(荷兰语passeren)。
- 释放资源时,需要对信号量原子加一,称为v操作(荷兰语Verhoog)
信号量的内核对象是一个信号量集(信号量的数组)
(二)信号量的作用
作用:主要用于同步进程(进程同步工作)。
- 二进制信号量:信号量的值如果只取0,1;
- 计数信号量:信号量的值大于1;
注意:
当进程获取资源时,信号量为0时,进行p操作进程会阻塞;
(三)信号量的能进程通信的原理
(四)信号量的操作的函数
所需头文件:
sys/types.h
sys/ipc.h
sys/sem.h
(1)创建或者获取已存在的信号量集
int semget(key_t key, int nsems, int semflg)
key
:两个进程使用相同的key值,就可以使用同一个信号量nsems
:内核维护的是一个信号量集,创建信号量集时,会指定信号量集的信号量个数(数组的大小),获取无所谓semflg
:可选IPC_CREAT没有key对应信号量就创建,存在则返回对应的信号量集的ID 、 IPC_EXCL(不管有没有该信号量都返回-1);IPC_CREAT | IP_EXCL没有则创建,有则返回-1返回值
:成功返回信号量集的ID,失败返回-1
注意:创建的信号量不手动销毁,系统内核就会一直帮我们维护下去,除非重启系统;
(2)操作信号量(初始化信号量的值、销毁信号量集)
int semctl(int semid, int semnum, int cmd, .../* union semun val*/ )
- semid:信号量ID
- semnum:信号量集的下标(操作第几个信号量)
- cmd:SETVAL(创建) 、 IPC_RMID(销毁)
- 返回值:成功0,失败-1
union semun联合体的结构体(需要自己声明)(都是4个字节):
union semun { int val; //设置的信号量的值 :临界资源的个数(一个打印机等) struct semid_ds *buf; //IPC_STAT, IPC_SET的缓冲区 unsigned short *array; //GETALL,SETALL的数组 struct seminfo *__buff; //IPC_INFO的缓冲区 };
(3) P V操作函数
int semop(int semid, struct sembuf *sops, size_t nsops);
-semid:信号量集ID
- struct sembuff *sops:指定p/v操作的buff
- size_t nsops :操作信号量的个数
//假设我们定义了一个大小为10的信号量集,现在要对其中的下标5和下标8信号量进行访问,进行PV操作,则需要修改struct sembuf中的元素; - 返回值:
第二个参数类型:(该类型已经存在sys/sem.h头文件中)
struct sembuf
{
unsigned short sem_num; /* semaphore number */ 5 / 8
short sem_op; /* semaphore operation */ -1是P操作 +1是V操作
short sem_flg; /* operation flags */IPC_NOWAIT非阻塞、SEM_UNDO阻塞
}
(五)信号量的具体操作
(1)void sem_init();
函数功能:创建/获取信号量集,初始化信号量的值
(2)void sem_p();
函数功能:实现对临界资源的P(-1)操作
(3)void sem_v();
函数功能:实现对临界资源的V(+1)操作
(4)void sem_destroy();
函数功能:销毁创建的信号量集(只销毁一次)
(六)一个进程同步的栗子
(1)未同步之前
jiege的代码:
#include <stdio.h>
#include <unistd.h>
#