共享内存
顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常为同一段物理内存。进程可以将同一段物理内存连接到他们自己的地址空间中,所有的进程都可以访问共享内存中的地址。
共享内存是系统出于多个进程之间通讯的考虑,而预留的的一块内存区。在/proc/sys/kernel/目录下,记录着共享内存的一些限制,如一个共享内存区的最大字节数shmmax,系统范围内最大共享内存区标识符数shmmni等,可以手工对其调整,但不推荐这样做。
- 共享内存是进程间共享数据的一种最快的方法。一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。
- 使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。若一个进程正在向共享内存区写数据,则在它做完这一步操作前,别的进程不应当去读、写这些数据。
共享内存有两种方式,即 shm 和 mmap 方式。前者直接共享物理内存,后者通过一个中间文件间接共享内存
共享内存打开创建
API
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg)
参数
- key : 用户给定的键值。
key标识共享内存的键值: 0/IPC_PRIVATE。 当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;如果key的取值为0,而参数shmflg中设置了IPC_PRIVATE这个标志,则同样将创建一块新的共享内存。
在IPC的通信模式下,不管是使用消息队列还是共享内存,甚至是信号量,每个IPC的对象(object)都有唯一的名字,称为“键”(key)。通过“键”,进程能够识别所用的对象。“键”与IPC对象的关系就如同文件名称之于文件,通过文件名,进程能够读写文件内的数据,甚至多个进程能够共用一个文件。而在IPC的通讯模式下,通过“键”的使用也使得一个IPC对象能为多个进程所共用。
- size : 申请内存的大小
size是要建立共享内存的长度。所有的内存分配操作都是以页为单位的。所以如果一段进程只申请一块只有一个字节的内存,内存也会分配整整一页(在i386机器中一页的缺省大小PACE_SIZE=4096字节)这样,新创建的共享内存的大小实际上是从size这个参数调整而来的页面大小。即如果size为1至4096,则实际申请到的共享内存大小为4K(一页);4097到8192,则实际申请到的共享内存大小为8K(两页),依此类推。
- shmflg :
shmflg主要和一些标志有关。其中有效的包括IPC_CREAT和IPC_EXCL,它们的功能与open()的O_CREAT和O_EXCL相当。
- IPC_CREAT 如果共享内存不存在,则创建一个共享内存,否则打开操作。
- IPC_EXCL 只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。
返回值
成功: 共享内存的标识符;
失败: -1,errno储存错误原因。
错误代码
EINVAL 参数size小于SHMMIN或大于SHMMAX。
EEXIST 预建立key所致的共享内存,但已经存在。
EIDRM 参数key所致的共享内存已经删除。
ENOSPC 超过了系统允许建立的共享内存的最大值(SHMALL )。
ENOENT 参数key所指的共享内存不存在,参数shmflg也未设IPC_CREAT位。
EACCES 没有权限。
ENOMEM 核心内存不足。
映射共享内存 – at:attach
API
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
- shmid : 由shmget()函数返回的共享内存标识
- shmaddr: 指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
- shmflg:一组标志位,通常为0。
返回值
成功: 一个指向共享内存第一个字节的指针
失败: -1,errno储存错误原因。
错误代码
EACCES : 没有权限
EIDRM : 共享内存已经被删除
EINVAL : invalid shmid value, or invalid shmaddr value, or can’t attach segment at shmaddr, or SHM_REMAP was specified and shmaddr was NULL.
ENOMEM : Could not allocate memory for the descriptor or for the page table
解除映射 – dt:detach
API
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
参数
- shmaddr : shmat()函数返回的地址指针
返回值
成功: 0
失败: -1,errno储存错误原因。
错误代码
EINVAL:There is no shared memory segment attached at shmaddr; or
shmaddr is not aligned on a page boundary
共享内存控制
API
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
- shmid : shmget()函数返回的共享内存标识符
- command是要采取的操作
command | 说明 |
---|---|
IPC_STAT | 设定 共享内存的属性 |
IPC_SET | 读取共享内存的属性 |
IPC_RMID | 删除共享内存,buf设置为NULL |
SHM_LOCK | 锁定共享内存段( 超级用户 )。 用于锁定内存,禁止内存交换。并不代表共享内存被锁定后禁止其它进程访问。 其真正的意义是:被锁定的内存不允许被交换到虚拟内存中。这样做的优势在于让共享内存一直处于内存中,从而提高程序性能。 |
SHM_UNLOCK | 解锁共享内存段。 |
注意:若选择删除共享内存,第三个参数传NULL
shmid_ds 构造体
struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */
size_t shm_segsz; /* Size of segment (bytes) */
time_t shm_atime; /* Last attach time */
time_t shm_dtime; /* Last detach time */
time_t shm_ctime; /* Last change time */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */
shmatt_t shm_nattch; /* No. of current attaches */
...
};
struct ipc_perm {
key_t __key; /* Key supplied to shmget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions + SHM_DEST and
SHM_LOCKED flags */
unsigned short __seq; /* Sequence number */
};
返回值
成功: 0
失败: -1,errno储存错误原因。
错误代码
EACCESS:参数cmd为IPC_STAT,确无权限读取该共享内存
EFAULT:参数buf指向无效的内存地址
EIDRM:标识符为msqid的共享内存已被删除
EINVAL:无效的参数cmd或shmid
EPERM:参数cmd为IPC_SET或IPC_RMID,却无足够的权限执行