可以说, 共享内存是一种最为高效的进程间通信方式, 因为进程可以直接读写内存, 不需要任何数据的复制。 为了在多个进程间交换信息, 内核专门留出了一块内存区, 这段内存区可以由需要访问的进程将其映射到自己的私有地址空间。 因此, 进程就可以直接读写这一内存区而不需要进行数据的复制, 从而大大提高了效率。 当然, 由于多个进程共享一段内存,因此也需要依靠某种同步机制, 如互斥锁和信号量等(请参考 4.7.2 节)。
其原理示意图如图4.7 所示。
先参考这个博客:
https://blog.csdn.net/tennysonsky/article/details/46425485
共享内存的实现分为两个步骤:
第一步是创建共享内存, 这里用到的函数是 shmget(),也就是从内存中获得一段共享内存区域;
第二步是映射共享内存, 也就是把这段创建的共享内存映射到具体的进程空间中, 这里使用的函数是 shmat()。
到这里, 就可以使用这段共享内存了, 也就是可以使用不带缓冲的 I/O 读写命令对其进行操作。
除此之外, 还有撤销映射的操作, 其函数为 shmdt()。 这里主要介绍这 3 个函数。
shmget()函数的语法要点。
函数原型
int shmget(key_t key, int size, int shmflg)
功能
创建获得共享内存
函数传入值
key: 共享内存的键值, 多个进程可以通过它访问同一个共享内存, 其中有个特殊值
IPC_PRIVATE, 用于创建当前进程的私有共享内存
size: 共享内存区大小
shmflg: 同 open()函数的权限位, 也可以用八进制表示法
函数返回值
成功: 共享内存段标识符
出错: 1
shmat()函数的语法要点。
函数原型
char *shmat(int shmid, const void *shmaddr, int shmflg)
函数传入值
shmid: 要映射的共享内存区标识符
shmaddr: 将共享内存映射到指定地址(若为 0 则表示系统自动分配地址并把该段共享内存映射到调用进程的地址空间)
shmflg:
- SHM_RDONLY: 共享内存只读
- 默认 0: 共享内存可读写
函数返回值
成功: 被映射的段地址
出错: 1
shmdt()函数的语法要点。
函数原型
int shmdt(const void *shmaddr)
函数参数shmaddr: 被映射的共享内存段地址
函数返回值
成功: 0
出错: 1
shmctl()函数的语法要点。
函数原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能
对已存在的共享内存进行控制
函数参数
shmid:共享内存标识
cmd:操作类型
buf:指向操作的信息
返回值
成功返回0,否则返回-1
cmd | 含义 |
IPC_STAT | 获取共享内存的状态 |
IPC_SET | 设置共享内存的权限 |
IPC_RMID | 删除共享内存 |
SHM_LOCK | 锁定共享内存,使共享内存不被置换出去 |
SHM_UNLOCK | 对共享内存 解锁 |
重要结构体:
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; /* 创建进程ID */
pid_t shm_lpid; /* 最近操作的进程ID */
shmatt_t shm_nattch; /* 建立映射的进程数 */
...
};
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 */
};