进程通信的前提:必须让不同进程看到同一份资源。
管道:文件缓冲区 共享内存:内存块 消息队列:队列
消息队列
让不同的进程看到同一个队列,允许不同的进程向内核发送带类型的数据块
带类型是为了区分数据块是由哪个类型区分的。
*共享内存和消息队列都是SystemV标准
共享内存接口和消息队列接口很像
消息队列接口
建立队列
控制消息队列
消息队列收发
msgp数据块地址,msgsz数据大小
查看消息队列/删除消息队列
ipcs -q
ipcrm -q msgid
信号量也类似
操作系统对ipc(各种通信方式)的管理
管理各自通信方式的结构体的第一个字段都是一样的
不同通信方式第一个字段struct ipc_perm都是一样的
存在一个数组对多种通信方式的struct ipc_perm管理。注意并不是通信方式结构体在数组中,而是通信方式结构体的第一个字段在数组中
当访问某个ipc资源时,通过确定ipc_perm*array的数组中ipc_perm中的key值确认是否资源是否创立。key值在结构体ipc_perm中
shmid,msgid实际上就是数组的下标
访问某个ipc资源的属性,通过地址强转成某个ipc类型,访问对应的属性。
操作系统没有直接管理各种类型ipc资源,而是通过给各种ipc资源相同的字段,通过字段和地址强转来实现各种类型ipc资源,类似于多态。
操作系统可以区分指针指向的对象类型。
shmid和fd不一样,不是从0开始了,这个数组不隶属于任何进程,由系统独立控制,shmid下标线性递增,直到最大值变成0。
互斥,临界资源,临界区
共享内存存在数据不一致问题:进程一写入部分数据时,进程二拿走大部分数据,导致双方发和收数据不完整。
通过加锁的方式来实现互斥访问,互斥-只允许一个执行流访问共享资源,进程二等进程一写完才能读。
共享的,任何时刻只允许一个执行流访问资源--临界资源,如管道。
访问临界资源的代码称为临界区
信号量/信号灯
信号量的本质是一把计数器,类似于 int cnt=n
描述临界资源中资源(共享且互斥)的数量多少
以电影院买票为例,电影票和座位映射,座位相当于共享资源,购买电影票相当于预定资源。
每卖出一张票,计算器就要减一,放映厅的资源就少一个。票数计算器减少到0,资源已经被申请完毕了。
把临界资源划分成多份,把临界资源把只需要访问的区域锁住。
通过引入一个计数器,直接让cnt--,表示申请资源,当cnt<=0,资源被申请完了,限制执行流申请。
申请计算器资源成功,表示具有访问资源的权限,本质上是对计数器资源的预订机制
计数器可以有效保证进入共享资源的执行流数量。
当多个执行流并发,执行流访问共享资源的一部分的时候,不是直接访问,而是先申请计数器资源。
资源为一的情况下,多个执行流只有一个执行流可以访问到-互斥
把值只能为1和0的计数器,叫做二元信号量,本质上是一个锁。
当计数器为一的情况下,其实将临界资源看成整体,整体申请,整体释放。
信号量本身也就是共享资源
多个执行流申请信号量资源,信号量减少必须是安全的。
cnt--:
c语言一条语句变成汇编三条语句
cnt变量 内存->cpu寄存器
cpu内进行--操作
将计算结果写回cnt变量内存位置
信号量变化是原子的
申请信号量,本质是对计数起-- P操作
释放资源,释放信号量,本质是对计数器进行++操作,v操作
申请和释放PV操作 --- 原子的!即两态的,要么完成,要么不做
(语句在执行时只有一条汇编语句)
这样是为了保持信号量变化是安全的
信号量值是1,0两态的,二元信号量,就是互斥功能。
SystemV版本信号量接口
信号量在进程充当flag作用,而不是传递大量数据,比如就像是前端的flag值
mmap函数也是共享内存,将内存和磁盘中的文件映射。向文件写入时不再通过read/write而是直接通过mmap写入