进程间通信二
共享内存
特性:
1.共享内存是最快的进程间通信方式
2.生命周期随内核
注意: 共享内存并没有自带同步与互斥----多个进程访问的时候存在安全问题
操作:代码操作流程/具体的代码 + 命令操作ipcs / ipcrm
本质原理:
在物理内存上开辟一块内存空间,多个进程可以将同一块物理内存映射到自己的虚拟地址空间,通过自己的虚拟地址直接访问这块空间,通过这种方式实现数据共享。
管道中的通信:涉及到两次用户态与内核态之间的数据拷贝;将数据写入管道,从管道读取数据。
共享内存中的通信:直接通过虚拟地址访问物理内存实现共享内存中的数据操作相较与管道这种通信少了两次用户态与内核态之间的数据拷贝操作,因此速度最快
共享内存的操作流程:
1. 创建共享内存----在物理内存上开辟空间
2. 进程将共享内存映射到自己的虚拟地址空间
3. 基本的内存操作都可以对这块空间进行操作
4. 不玩了,解除虚拟地址空间与共享内存的映射关系
5. 释放共享内存资源
(1). 创建共享内存:
int shmget(key_t key, size_t size, int shmflg);
①key:内核中共享内存的标识符----多个进程通过相同标识符才能打开同一块共享内存
②.size:共享内存大小----以内存页为单位进行分配
③.shmflg:IPC_CREAT - 存在则打开,不存在则创建 | IPC_EXCL-与 IPC_CREAT同时使用,若文件存在则报错,不存在则创建 | mode-权限
返回值: 返回一个非负整数----共享内存的操作句柄;失败返回-1;
④.生成一个key值:
key_t ftko(const char* pathname, int proj_id)
通过inode节点号和projid合成一个key
(2).将共享内存映射到自己的虚拟地址空间
void *shmat(int shmid, const void* shamddr, int shmflg)
①.shmid:shmget返回的共享内存操作句柄
②.shmaddr:共享内存映射在虚拟地址空间的首地址----通常置NULL;
③.shmflg:映射成功之后对共享内存可以进行的操作 SHM_RDONLY用于只读(前提是要有读 的权限)/ 0——默认可读可写
返回值:返回共享内存映射在虚拟地址空间的首地址----通过这个首地址进行后续内存操作。失败返回-1。
(3).解除映射关系:
int shmdt(const void* shmaddr)
①.shmmaddr:映射在虚拟地址空间的首地址
成功返回0; 失败返回-1;
(4).释放共享内存:
shmctl(int shmid, int cmd, struct shmid_ds* buf)
①.shmid:共享内存操作句柄
②.cmd:对共享内存想要进行的操作
③.buf:用于获取/设置共享内存信息的结构
成功返回0;失败返回 -1;
操作系统中进程间通信资源的命令操作:
ipcs: 查看进程间通信资源
-m 查看共享内存
-q 查看消息队列
-s 查看信号量
ipcrm: 删除进程间通信资源
-m 删除共享内存
-q 删除消息队列
-s 删除信号量
共享内存被删除的时候,并不会立即删除,只是将其状态置为被销毁状态,移除标识,为了不让这个共享内存继续被其他进程映射连接,然后等到当前共享内存映射数为0的时候,才会真正删除这块共享内存
共享内存数据的写入,是一种针对地址指向空间的覆盖式写入
shm_write.c
#include<stdio.h>
#include<sys/shm.h>
#include<string.h>
#define IPC_KEY 0X12345678
int main(){
//1.创建共享内存
//int shmget(标识符,大小, 标志位|权限)
int shm_id = shmget(IPC_KEY, 32, IPC_CREAT|0664);
if(shm_id < 0){
perror("shmget error");
return -1;
}
//2.建立映射关系
//shmat(操作句柄,映射首地址,操作权限)
void* shm_start = shmat(shm_id, NULL, 0);
if(shm_start == (void*)-1){
perror("shmat error");
return -1;
}
//3.进行内存操作
int i = 0;
while(1){
sprintf(shm_start, "%s + %d", "nihaoya~", i++);
sleep(1);
}
//4.解除映射关系
//shmdt(映射首地址)
shmdt(shm_start);
//5.释放内存共享
//shmctl(操作句柄, 要进行的操作-IPC_RMID, 共享内存信息结构)
shmctl(shm_id, IPC_RMID, NULL);
return 0;
}
shm_read.c
#include<stdio.h>
#include<sys/shm.h>
#include<string.h>
#define IPC_KEY 0X12345678
int main(){
//1.创建共享内存
//int shmget(标识符,大小, 标志位|权限)
int shm_id = shmget(IPC_KEY, 32, IPC_CREAT|0664);
if(shm_id < 0){
perror("shmget error");
return -1;
}
//2.建立映射关系
//shmat(操作句柄,映射首地址,操作权限)
void* shm_start = shmat(shm_id, NULL, 0);
if(shm_start == (void*)-1){
perror("shmat error");
return -1;
}
//3.进行内存操作
while(1){
printf("[%s]\n", shm_start);
sleep(1);
}
//4.解除映射关系
//shmdt(映射首地址)
shmdt(shm_start);
//5.释放内存共享
//shmctl(操作句柄, 要进行的操作-IPC_RMID, 共享内存信息结构)
shmctl(shm_id, IPC_RMID, NULL);
return 0;
}
消息队列
内核中的一个优先级队列;多个进程通过访问同一个队列,进行添加节点或者获取节点实现通信
1. 创建消息队列----在内核中创建一个优先级队列
int msgget(key_t key, int msgflg);
2.进程可以向队列中添加节点/获取节点
int msgsnd(int msgid, const void* msgp, size_t msgsz, int msgflg)
ssize_t msgrcv(int msgid, void* msgsz, long msgtyp, int msgflg);
3.删除消息队列
int msgctl(int msgid, int cmd, struct msqid_ds* buf)
特性:
1.自带同步与互斥
2.生命周期随内核
信号量:
是用于实现进程间同步与互斥的(共享内存本身是不提供同步与互斥的,操作存在安全隐患,因此需要使用信号量来保护对共享内存的操作)
同步: 通过同一时间的唯一访问实现临界资源访问的安全性
互斥:一个内核中的计数器+PCB等待队列
信号量实现互斥:
本质:计数器+等待队列+使进程等待/唤醒的接口
信号量通过自身的计数器+等待队列以及使一个进程等待以及一个进程唤醒的操作组成
互斥的实现:
保证同一时间只有一个进程能够访问资源
只要保证信号量的资源计数不会大于1就可以实现
eg:
假设停车场只有一个停车位,我的车停了,则计数变为0,其他的车都没法停;我的车出来则+1;
进程互斥实现思想:以资源只有一份,只有一个进程能够获取;用完了放回来;下一个进程再获取的思想。
同步的实现:
通过计数器对资源进行计数;计数>0表示能获取;计数<=0表示不能获取通过等待接口使进程等待加入等待队列;等到有资源释放的时候唤醒
eg:
停车场----告示牌----计数器----统计停车场中空闲车位的数量
假如我有车,去停车,通过告示牌上的计数器来判断是否有空闲停车位,如果计数器>0然后我再去停车。
计数>0才能访问;获取一个资源,计数-1。计数<=0则不能访问;则计数-1;然后PCB状态值为可中断 休眠状态,加入等待状态。其他进程产生了资源,计数+1;若计数>0则什么都不敢;若计数<0则从等待队列中唤醒一个PCB去获取资源。
资源能访问的时候,让你访问;不能访问的时候,让你等着;等到能够访问的时候再去唤醒你去访问