共享内存原理
简而言之,就是两个进程指向了同一块物理空间。(它们都能看到同一块内存)
共享内存在内核中同时可以存在很多个,OS要管理所有的共享内存。
如何保证两个不同进程看到的是同一个共享内存呢???要给共享内存提供唯一性标识(后文提到的key)!!!
使用共享内存通信,一定是一个进程创建新的shm,另一个直接获取共享内存即可。
类比:共享内存 vs 文件操作
共享内存,如果进程结束,我们没有主动释放它,则共享内存一直存在。——共享内存的生命周期随内核。(除非重启系统,否则共享内存一直存在)。
文件操作,一个进程打开一个文件,进程退出时,这个被打开的文件就会被系统自动释放掉。——文件的生命周期随进程。
shmget 系统调用函数
#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);
参数说明
第一个参数 key
1.这个key意义是什么?怎么形成的?
意义:标识共享内存的唯一性。
如何形成:由用户随意指定key值。
2.为什么要让用户传入?
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
参数由用户指定,由ftok函数的一套算法生成一个key,只要两个进程约定使用同样的字符串,同样的数字,就可以生成同样的key,从而标识同一块内存。
第二个参数 size
指定共享内存段的大小(以字节为单位)。如果是在获取一个已经存在的共享内存段,这个参数可以设置为 0。
注意在内核中共享内存的大小时以4kb为基本单位的。
例子:使用ipcs -m
指令,查看共享内存。(这里申请了4096b大小)
开辟共享内存是向上取整的,例如申请1b,实际上申请了4096b,还有4095b不能使用被浪费了,申请4097b,实际上申请了2×4096b,还有4095b不能使用被浪费了。
所以建议申请大小为n×4 kb。
第三个参数 shmflg
IPC_CREAT:如果共享内存不存在就创建,如果共享内存已经存在,就直接获取它
IPC_EXCL:不能单独使用,没意义。
IPC_CREAT | IPC_EXCL:如果共享内存不存在就创建,如果共享内存已经存在,出错返回!(如果创建成功,一定是全新的共享内存)。
IPC_CREAT | 八进制权限:赋予权限。
关于共享内存的权限
使用shmget(key, size, IPC_CREAT | IPC_EXCL);
使用shmget(key, size, IPC_CREAT | IPC_EXCL| 0666);
通过按位或的方式给共享内存加权限。
实际上第三个标志位的标志定义如下图,最右边三位始终是0,这就给权限位留出了空间。
删除共享内存
指令删除
ipc指令
ipcs
查看系统中指定用户创建的共享内存,消息队列,信号量。
ipcs -m
查看系统中指定用户创建的共享内存
使用 ipcrm -m shmid 删除共享内存
key vs shmid
key:在内核的角度,区分shm的唯一性。(类似于struct file*)
shmid:指令级,代码级,最后对共享内存进行控制,用的都是shmid(类似于文件描述符fd)
shmdt
代码删除
shmctl系统调用函数
#include <sys/ipc.h>
#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);
第一个参数 int shmid:
共享内存的标识符
第二个参数 int cmd:
控制命令。常用的命令包括:
IPC_STAT: 获取共享内存段的状态信息,并将其存储在 buf 指向的 shmid_ds 结构中。
IPC_SET:
设置共享内存段的权限和其他属性(如 UID、GID、模式等),使用 buf 中的数据。
IPC_RMID
:(用于删除共享内存)
标记共享内存段为已删除。当最后一个进程分离该共享内存段时,它将被真正删除。
IPC_INFO (Linux 特有):
获取系统级别的共享内存信息。 SHM_INFO (Linux 特有): 获取共享内存资源的使用情况。
SHM_STAT (Linux 特有): 类似于 IPC_STAT,但通过索引访问共享内存段。
第三个参数buf:获取共享内存的相关属性
- 指向
shmid_ds
结构的指针,用于存储或提供共享内存段的状态信息。 - 如果
cmd
是IPC_RMID
,则可以设置为 NULL。
shmid_ds
定义如下:
struct shmid_ds {struct ipc_perm shm_perm; // 权限信息size_t shm_segsz; // 共享内存段的大小time_t shm_atime; // 最后附加时间time_t shm_dtime; // 最后分离时间time_t shm_ctime; // 最后修改时间pid_t shm_cpid; // 创建进程的 PIDpid_t shm_lpid; // 最后操作进程的 PIDshmatt_t shm_nattch; // 当前附加的进程数
};
返回值
成功时,返回值取决于 cmd:
- 对于 IPC_STAT、IPC_SET 和 IPC_RMID,成功时返回 0。
- 对于 IPC_INFO 和 SHM_INFO,返回内核中共享内存段的最大索引值。
- 对于 SHM_STAT,返回共享内存段的标识符。
失败时,返回 -1,并设置 errno 来指示错误类型。
例子:
代码删除共享内存
void DeleteShm(int shmid)
{int n = shmctl(shmid, IPC_RMID, NULL);if (n < 0){cerr << "shmctl error" << endl;}else{cout << "shmctl delete shm success ,shmid: " << shmid << std::endl;}
}