共享内存
共享内存指 (shared memory)在多处理器的计算机系统中,可以被不同中央处理器(CPU)访问的大容量内存。由于多个CPU需要快速访问存储器,这样就要对存储器进行缓存(Cache)。任何一个缓存的数据被更新后,由于其他处理器也可能要存取,共享内存就需要立即更新,否则不同的处理器可能用到不同的数据。共享内存是 Unix下的多进程之间的通信方法 ,这种方法通常用于一个程序的多进程间通信,实际上多个程序间也可以通过共享内存来传递信息。
这里可以看到我们进程A和进程B同时去映射到内存中 那么这个内存就叫做共享内存
共享内存的内存确实是物理地址 但是我们只是共享了它的映射关系 而不是对这份内存进行拷贝
一个进程在自己的页表中,将该空间和进程地址空间上的共享区的一块地址空间形成映射关系。另外一进程在页表上,将同一块物理空间和该进程地址空间上的共享区的一块地址空间形成映射关系。
因此共享内存相比其他几种方式有着更方便的数据控制能力,数据在读写过程中会更透明。当成功导入一块共享内存后,它只是相当于一个字符串指针来指向一块内存,在当前进程下用户可以随意的访问。缺点是,数据写入进程或数据读出进程中,需要附加的数据结构控制。
进程间通信方式有哪些?
进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。IPC 的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams 等。其中 Socket 和 Streams 支持不同主机上的两个进程 IPC。
管道
它是半双工的,具有固定的读端和写端;
它只能用于父子进程或者兄弟进程之间的进程的通信;
它可以看成是一种特殊的文件,对于它的读写也可以使用普通的 read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
命名管道
FIFO 可以在无关的进程之间交换数据,与无名管道不同;
FIFO 有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。
消息队列
消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符 ID 来标识;
消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级;
消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除;
消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
信号量
信号量(semaphore)是一个计数器。用于实现进程间的互斥与同步,而不是用于存储进程间通信数据;
信号量用于进程间同步,若要在进程间传递数据需要结合共享内存;
信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作;
每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数;
支持信号量组。
共享内存
共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区;
共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。
这个是我在刷面试经历时遇到了其他博主对于c++常面试到中截取到关于共享内存的描述
这里提到了我们的共享内存是最快的一种IPC 也就是进程间通信的方式
其他几种方式在之前的学习中我们也有了解
这就是整个共享内存的流程
和管道类似
我们一端进行写入 然后在另外一端进行读操作
消息队列
消息队列(英语:Message queue)是一种进程间通信或同一进程的不同线程间的通信方式,软件的贮列用来处理一系列的输入,通常是来自用户。
消息队列提供了异步的通信协议,每一个贮列中的纪录包含详细说明的数据,包含发生的时间,输入设备的种类,以及特定的输入参数,也就是说:消息的发送者和接收者不需要同时与消息队列交互。消息会保存在队列中,直到接收者取回它。
接下来我们用编程的方式加深对消息队列的了解
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/msg.h>
typedef struct mess{
long mtype; //消息类型
char mtext[128]; //消息内容
}msgdata;
int main(){
//1. 获取或创建消息队列
int msgid = msgget((key_t)1234,IPC_CREAT|0600);
if(msgid == -1) exit(1);
//2. 从消息队列获取数据
msgdata data;
msgrcv(msgid,&data,128,1,0); //读取1号消息
// b获取消息,不存在1消息,阻塞
// msgrcv(msgid,&data,128,1,IPC_NOWAIT); //不存在消息当前进程不阻塞。
// msgecv(msgid,&data,128,0,0); // 0 -> 任意消息类型
printf("data type: %ld\n",data.mtype);
printf("data text:%s\n",data.mtext);
//msgctl(msgid,IPC_RMID,0);
exit(0);
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/msg.h>
typedef struct mess{
long mtype; //消息类型
char mtext[128]; //消息内容
}msgdata;
int main(){
//1. 获取或创建消息队列
int msgid = msgget((key_t)1234,IPC_CREAT|0600);
if(msgid == -1) exit(1);
//2. 向消息队列发送数据:从键盘获取字符串
msgdata data;
data.mtype = 2;
strcpy(data.mtext,"hello2");
msgsnd(msgid,&data,128,0);
exit(0);
}
这里我们要了解消息队列的特点
如果我们在一端规定只读取一号消息
那么我们在另外一端进行写操作时 写入的其他号的消息
是没有办法被读到的
同时还会引发消息队列中读端的阻塞
这就是我们目前学习到的进程间通信的几种方式
包括 管道 消息队列 共享内存以及信号量
其中管道涉及到了同步 和临界资源和临界区