Java没有实现,这里用C/C++讲解

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()函数要么返回一个已经存在的共享内存的操作符,要么返回一个新建的共享内存的标识符。

    1. 如果将IPC_CREATIPC_EXCL标志一起使用,shmget()将返回一个新建的共享内存的标识符;如果该共享内存已存在,则返回-1
    2. IPC_EXEL标志本身并没有太大的意义,但是和IPC_CREAT标志一起使用可以用来保证所得的对象是新建的,而不是打开已有的对象。

真正的用法是将上述的标识符合权限标识(数字)用或运算组合

  1. IPC_CREAT | 0777
  2. 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


  1. shmid_ds数据结构表示每个新建的共享内存。当shmget()创建了一块新的共享内存后,返回一个可以用于引用该共享内存的shmid_ds数据结构的标识符。
  1. #include </linux/shm.h>
  2. struct shmid_ds {
  3. struct ipc_perm shm_perm; /* operation perms */
  4. int shm_segsz; /* size of segment (bytes) */
  5. __kernel_time_t shm_atime; /* last attach time */
  6. __kernel_time_t shm_dtime; /* last detach time */
  7. __kernel_time_t shm_ctime; /* last change time */
  8. __kernel_ipc_pid_t shm_cpid; /* pid of creator */
  9. __kernel_ipc_pid_t shm_lpid; /* pid of last operator */
  10. unsigned short shm_nattch; /* no. of current attaches */
  11. unsigned short shm_unused; /* compatibility */
  12. void *shm_unused2; /* ditto - used by DIPC */
  13. void *shm_unused3; /* unused */
  14. };

struct ipc_perm


  1. 对于每个IPC对象,系统共用一个struct ipc_perm的数据结构来存放权限信息,以确定一个ipc操作是否可以访问该IPC对象。
  1. struct ipc_perm {
  2. __kernel_key_t key;
  3. __kernel_uid_t uid;
  4. __kernel_gid_t gid;
  5. __kernel_uid_t cuid;
  6. __kernel_gid_t cgid;
  7. __kernel_mode_t mode;
  8. unsigned short seq;
  9. };

ps:也可使用 ipcrm -m shmid的形式删除共享内存,但是如果有其他的进程在使用共享内存,则不会真正的删除共享内存,但会把共享内存的状态(使用ipcs -m查看status)制为dest,该动作是系统维护的。此时共享内能可以使用,当最后一个的进程结束或是不挂载共享内存时,共享内存则会自动删除。