文章目录
- 共享内存的概要
- 创建共享内存
- shmget()参数key
- shmget()参数size
- shmget()参数shmflg
- 删除共享内存
- 挂载共享内存
- 去关联
共享内存的概要
共享内存允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间传递数据的一种非常有效的方式。不同进程之间共享的内存安排为同一段物理内存。
共享内存是由IPC为进程创建的一个特殊的地址范围,它将出现在该进程的地址空间中。其他进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc()分配的内存一样。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。
特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量。
两个进程的PCB各自维护着一个进程地址空间。当两个进程要进行通信时:
操作系统在内存中开辟一个内存块。
通过两个进程的页表,将内存中的内存块映射到两个进程的进程地址空间中。
此时两个进程便建立了连接。
进行通信时,两个进程只需要访问自己的进程地址空间即可,操作系统会通过页表访问内存中的内存块。
所以说,共享内存就是让不同的进程,看到同一块内存块。
所以说,共享内存就是让不同的进程,看到同一块内存块。
在维持通信关系中,还涉及到几个概念:
挂接:将内存中创建好的内存块映射到进程的地址空间中。
去关联:不想通信时,取消进程和内存的映射关系。
注意: 去关联后,共享内存仍然存在,只是和去关联的进程没有了映射关系。
共享内存区是最快的IPC(进程间通信)形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。
创建共享内存
#include <sys/shm.h>
void *shmat(int shm_id, const void *shm_addr, int shmflg);
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
int shmdt(const void *shm_addr);
int shmget(key_t key, size_t size, int shmflg);
shmget()函数用来创建共享内存,它的原型为:
int shmget(key_t key, size_t size, int shmflg);
创建成功返回一个非负整数,即共享内存标识符;失败返回-1。
共享内存多了,就需要有一个标识,让要通信的进程找到正确的共享内存。
shmget()参数key
key值就是共享内存的标识,让想要通信的进程双方看到同一块公共资源。
系统中既然存在很多个共享内存,操作系统势必要将它们管理起来,管理也是使用先描述再组织的方式。因此管理共享内存并不是在管理内存块本身,而是在管理共享内存对应的结构体
问题又来了,怎么保证这个key值是唯一的呢?
从shmget函数的声明中可以看到,这个key值是我们传给操作系统的,也就是用我们传的key值来标定共享内存。
ftok()函数来生成一个独一无二的key值。
shmget()参数size
size是用来指定开辟的共享内存是多大的,以字节为单位。
一般指定的大小是4KB的整数倍。
也可以是任意值。
操作系统在开辟共享内存的时候是以4KB为单位的。每次开辟的共享内存,最小也是4KB的。
假设我们指定4097字节大小的共享内存,但是在内存中实际开辟的共享内存是2*4KB的。
但是在使用的时候只能使用4097字节的空间,剩下的空间用户无法使用,操作系统也不会用,就浪费掉了。
shmget()参数shmflg
这是一个标志位,和之前使用的open用法相似,也是一个int类型的数据,根据比特位不同,用法也不同。
常用的两个选项:
IPC_CREAT:创建共享内存,如果不存在,创建新的,如果存在,获取相关信息。
IPC_EXCL:无法单独使用,必须与其他标志组合使用。
IPC_CREAT | IPC_EXCL:创建共享内存,如果不存在,则创建,如果存在,错误返回。
例如:
在语言模型中,编码器和解码器都是由一个个的 Transformer 组件拼接在一起形成的。
删除共享内存
指令:ipcrm -m shimd
功能:删除指定shimd标识的共享内存。
shmid:获取共享内存后返回的标识符。
cmd:指定控制共享内存的方式。
buf:描述共享内存的数据结构指针。
挂载共享内存
shmat是让进程和共享内存挂接:
shmid:创建共享内存后返回的标识符。
shmaddr:指定共享内存映射到进程地址空间中的地址,一般设置成NULL,让系统自动来设置。
shmflg:不用管它是啥,直接给0。
返回值:共享内存映射到进程地址空间中的地址。不成功返回-1,但是是void*类型的。
在运行的时候发现报错了,说我们没有权限,再查看共享内存,发现确实是创建了,但是共享内存的权限是0,也就是我们谁都不能访问。
解决办法就让给共享内存开发相应的权限:
去关联
shmdt是让进程和共享内存去关联。
shmaddr:要去关联的共享内存映射在进程地址空间中的起始地址。
返回值:成功返回0,不成功返回-1。