1.基本原理
共享内存的原理很简单,就是开辟一段物理内存,然后可以让进程的虚拟内存一部分都映射到这同一块内存。
这个是Linux内核的功能,IPC(进程通信)的一部分
关于虚拟内存的概述,mmap介绍里有详细解释。
2.实现方式
2.1函数
int shmget(key_t key, size_t size, int shmflg)
开辟内存(获取内存Id)
key_t key:内存关键字,相当于唯一Id,可以用0或者IPC_PRIVATE来匿名创建。自定义通常使用ftok()函数来想系统获取key值,这个key其实是IPC对象的key,IPC系统有不同的实现,MQ和SM相应的IPC对象都有这个key。
//一般用法 IPC_PRIVATE//匿名创建
define IPCKEY 0x344378 //宏定义
ftok(pathname,0x03)//通过设定一个文件路径,再提供一个八进制数字想系统获取,原理是将数字和文件索引号十六进制拼接,如果文件删除或变化就,key就会变化,所以推荐宏定义。
size_t size 申请的内存大小,字节为单位,但是大小为页大小的倍数,参数可以不是倍数,但返回的大小会补成倍数
- int shmflg 这个是共享内存的标识:它们的功能与open()的O_CREAT和O_EXCL相当
IPC_CREAT 如果共享内存不存在,则创建一个共享内存,否则打开操作。 IPC_EXCL 只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。
如果单独使用IPC_CREAT,shmget()函数要么返回一个已经存在的共享内存的操作符,要么返回一个新建的共享内存的标识符。如果将IPC_CREAT和IPC_EXCL标志一起使用,shmget()将返回一个新建的共享内存的标识符;如果该共享内存已存在,则返回-1。
IPC_EXEL标志本身并没有太大的意义,但是和IPC_CREAT标志一起使用可以用来保证所得的对象是新建的,而不是打开已有的对象。
真正的用法是将上述的标识符合权限标识(数字)用或运算组合
IPC_CREAT | 0777
IPC_CREAT | 0600
这个权限标识就是Linux当中文件的权限数字标识,这里不做解释了。
void shmat(int shmid, const void shmaddr, int shmflg);
通过Id获取指针
shmid:上面的函数返回的就是内存的Id
shmaddr:指定共享内存出现在进程内存地址的什么位置,通常设为NULL,内核自行选择
shmflg:SHM_RDONLY为只读模式,其他为读写模式
返回:内存地址头部指针
int shmdt(const void *shmaddr);
释放进程与内存的绑定
shmaddr:上面函数返回的指针
返回:0成功,-1出错,原因会在errno中
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
对共享内存的一些操作:
shmid:Id,不消解释
cmd:
IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中
IPC_SET:改变共享内存的状态,把buf所指的shmid_ds结构中的uid、gid、mode复制到共享内存的shmid_ds结构内
IPC_RMID:删除这片共享内存
buf:共享内存管理结构体。具体说明参见共享内存内核结构定义部分
共享内存不会随着程序结束而自动消除,要么调用shmctl删除,要么自己用手敲命令ipcrm去删除,否则永远留在系统中。
2.2部分数据结构
struct shmid_ds
shmid_ds数据结构表示每个新建的共享内存。当shmget()创建了一块新的共享内存后,返回一个可以用于引用该共享内存的shmid_ds数据结构的标识符。
#include </linux/shm.h>
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
__kernel_time_t shm_atime; /* last attach time */
__kernel_time_t shm_dtime; /* last detach time */
__kernel_time_t shm_ctime; /* last change time */
__kernel_ipc_pid_t shm_cpid; /* pid of creator */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
unsigned short shm_nattch; /* no. of current attaches */
unsigned short shm_unused; /* compatibility */
void *shm_unused2; /* ditto - used by DIPC */
void *shm_unused3; /* unused */
};
struct ipc_perm
对于每个IPC对象,系统共用一个struct ipc_perm的数据结构来存放权限信息,以确定一个ipc操作是否可以访问该IPC对象。
struct ipc_perm {
__kernel_key_t key;
__kernel_uid_t uid;
__kernel_gid_t gid;
__kernel_uid_t cuid;
__kernel_gid_t cgid;
__kernel_mode_t mode;
unsigned short seq;
};
ps:也可使用 ipcrm -m shmid的形式删除共享内存,但是如果有其他的进程在使用共享内存,则不会真正的删除共享内存,但会把共享内存的状态(使用ipcs -m查看status)制为dest,该动作是系统维护的。此时共享内能可以使用,当最后一个的进程结束或是不挂载共享内存时,共享内存则会自动删除。