目录
进程间通信的本质及前提:让不同的进程看到同一份资源!
共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。
什么是共享内存?
一个 进程 A 通过系统调用 shmget 在物理内存中开辟一块空间,并通过页表映射到进程地址空间中,其他的进程也通过调用 shmget,并设置不同参数,就可以实现在物理内存中找到 进程 A 开辟的空间,并将这块空间通过页表映射到自己的进程地址空间中。自此,这多个进程通过访问自己的进程地址空间,就可以访问这块物理内存中的空间,也就实现了让不同的进程看到同一份代码!
那么其他进程是如何做到可以找到进程 A 创建的共享内存的呢?
因为操作系统中一定会允许存在多个共享内存,所以操作系统也要对这些共享内存做管理,因此一定会有描述这些共享内存的结构体,而每个独一的共享内存都会有一个标识 key,这也是创建和寻找它的唯一标志,这个 key 参数会在 shmget 调用中使用。所以其他的进程只需要在访问共享内存的时候使用 key,就能找到进程 A 当时创建的共享内存,进而进行访问。
共享内存的系统调用接口
shmget 接口:创建/获取共享内存
int shmget(key_t key, size_t size, int shmflg);
一个随机并不重复的 key 值可以通过 ftok 接口产生
size 是需要创建的共享内存的大小,shmflg 创建/获取共享内存时传入的标志位。
其中,参数shmflg可以为 IPC_CREAT 和 IPC_EXCL || IPC_CREAT
- IPC_CREAT:shm 如果不存在,就创建,如果存在,就获取!通常用于获取时用。
- IPC_EXCL || IPC_CREAT:shm 如果不存在就创建,如果存在就出错返回,这样可以保证创建的共享内存是全新的!通常用于创建时用。IPC_EXCL 不能单独使用。
创建共享内存
int shmid = shmget(key, 4096, IPC_EXCL || IPC_CREAT);
获取共享内存:
int shmid = shmget(key, 4096, IPC_CREAT);
如图,创建/获取 共享内存的工作就完成了。
shmat 接口:将共享内存映射到进程地址空间中
void *shmat(int shmid, const void *shmaddr, int shmflg);
这个接口有点像C语言中的 malloc 函数,都是在进程地址空间中申请一块空间,并返回 void* 类型的指向对应空间的指针,如果要这块内存的话,就需要将 void* 强转为对应数据类型的指针。
其中,shmaddr 参数是映射到地址空间中的地址,可以自己设置,也可以直接传 nullptr,操作系统会自动分配空间。
shmflg 原理与 shmget 是相同的,这里直接用 0 就行!
shmdt 接口:将共享内存从进程地址空间中移除
int shmdt(const void *shmaddr);
这个接口的使用比较简单,只需要将上一步得到的共享内存在进程地址空间中的起始地址作为参数传给 shmdt 即可将共享内存从进程地址中移除!
shmdt(s);
shmctl接口:销毁共享内存
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
查看当前共享内存的信息
ipcs -m
在此之后,就可以在不同进程中访问共享内存中的空间,进而达到通信的目的!
共享内存
共享内存是进程间通信速度最快的,并且可以提供较大的空间,但共享内存不会提供同步机制,里面的信息是直接裸露给所有使用者的,因此也必须注意共享内存的数据安全问题。
共享内存相对于管道通信不会涉及大量的数据拷贝工作,是一个比较高效的进程间通信模式。