前言:进程间的通信方式包括
IPC:
1、管道
pipe 无名管道
fifo 有名管道
2、信号 signal
3、消息队列 System V消息队列 / POSIX消息队列
4、共享内存 System V共享内存 / POSIX共享内存
5、信号量 System V信号量 / POSIX信号量 <-----------
6、socket套接字
1、引入
例子:
int i = 0;
func()
{
i++;
}
当有两个任务,调用func()函数时,执行之后i的值为多少?
答: 不确定的,因为i++ 是一个非原子操作
可以分割成3个步骤:
(1)读取
(2)计算
(3)回写
====================
ps. 原子操作 Atomic Operation
一般定义为 在执行过程中不会被线程调度机制中断的操作
这种操作一旦开始 就会一直运行到结束,中间不会被其他的线程的操作所打断
原子操作是不可分割的,即操作要么完全执行,要么完全不执行,不存在执行一半的情况
非原子操作与之相反
综上所述如果有两个或者多个任务(进程/线程),去访问同一个共享资源(硬件/软件) 那么就必须要保证这个共享资源的 被有序访问。
2、信号量 semaphore
信号量
是用于 不同进程之间 或者 同一个进程中不同线程之间 的同步机制
同步 ---> 有序、有条件的访问
信号量的出现,就是为了保护共享资源,让共享资源被有序的访问
信号量 是用来表示一种资源的数量
是一种特殊的计数器,用于控制对共享资源的访问
什么时候需要信号量?
需要保护对象的时候,就需要信号量
“保护”:让被保护的对象(共享资源)被有序访问
3、如何保护?
信号量机制 其实是对程序设计者的约束,用来保护共享资源的
例子:
进程A 进程B 都需要访问同一个“互斥”设备
那么就用一个信号量来表示能不能访问该设备,然后每一个进程在访问这个设备之前,
都先去访问这个信号量:
如果能够访问该设备,就先将信号量调成“NO”,然后再去访问这个互斥设备,
访问完之后,最后再将该信号量调成“YES”
信号量如何实现?
一个进程(或线程)在某一个信号量上 执行3个操作:
(1)创建一个信号量,还必须要求 调用者指定信号量的初始值
初始值就表示给信号量保护的共享资源 可以被多少个任务访问
sem --> 1 表示可以被1个进程或者线程去访问
sem --> 5 表示可以被5个进程或者线程去访问
(2)等待一个信号量,该操作会测试这个信号量的值
如果其值小于或者等于0 那么就会阻塞(等待)
一旦它的值变成大于0 就会先将它-1 在继续执行访问它的代码
while( sem_value <= 0 )
{
wait
}
sem_value --;
访问
p操作 proberen (尝试 荷兰语)
lock 上锁
(3)释放一个信号量,将信号量的值+1
sem_value ++;
v操作 verhogen (增加)
unlock 解锁
信号量保护:
在临界区的前面 加上一个p操作,然后在临界区的后面 加上一个v操作
“临界区”: 把操作“共享资源”的代码区域 称为 临界区
p
xxx //临界区
v
4、信号量的实现
System V semaphore
POSIX semaphore
POSIX 有名 semaphore
POSIX 无名 semaphore
4.1)System V semaphore
System V 信号量 ---> 计数信号量集 (信号量数组)
存在于 内核中
对于系统中的每个信号量集,内核都会维护在如下的结构体中:
struct semid_ds
{
struct ipc_perm sem_perm; /* 权限 Ownership and permissions */
time_t sem_otime; /* 最后调用semp()的时间 Last semop time */
time_t sem_ctime; /* 最后修改的时间 Last change time */
unsigned long sem_nsems; /* 信号量数组中有多少信号量 No. of semaphores in set */
};
接口函数:
(1)获取键值key
ftok()
(2)创建或者打开一个System V信号量集
semget()
NAME
semget - get a System V semaphore set identifier
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
功能:创建或者打开一个System V信号量集
参数:
key:键值
nsems:指定要创建的信号量集中 信号量的个数
semflg: 标志位
(1)创建标志
IPC_CREAT | 权限位
例子:
IPC_CREAT | 0666
注意:
如果创建失败的原因 是因为已经存在了
且 创建的标志为 IPC_CREAT | IPC_EXCL 一起使用
此时 errno == EEXIST
(2)打开标志
0
返回值:
成功,返回该信号量集的id
失败,返回-1,同时errno被设置
注意:
在创建一个信号量集时,信号量的值是不确定的
因此 我们在创建一个信号量集之后,要马上指定它们的初始值
(3)控制操作
semctl
NAME
semctl - System V semaphore control operations
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
功能:对System V信号量集的控制操作
参数:
semid: 信号量集id
semnum:指定要操作哪个信号量,就是信号量数组的下标(0,1,2,...)
cmd: 命令号
IPC_RMID 删除
IPC_STAT 获取属性
IPC_SET 设置属性
GETVAL 获取某个信号量的值
SETVAL 设置某个信号量的值
GETALL 获取所有信号量的值
SETALL 设置所有信号量的值
...
...:第四个参数,根据cmd的不同,第四个参数也不相同
第四个参数的类型:
union semun
{
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
&nb