SystemV共享内存
我们上一篇所谈的管道,不管是匿名还是命名,本质都是基于文件的,而下面的System V共享内存是一种用于在Linux系统中进程间共享内存的,是操作系统特地设计的一种通信方式
但本质都是让不同的进程看到同一份资源
共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据
原理:
- 首先,在物理内存中申请一块资源
- 其次,将这块资源与各个进程的页表建立影视
- 最后,在共享区中开辟空间并将虚拟地址填充到各自页表的对应位置
进程便看到了同一份物理内存,这块物理内存就叫做共享内存
共享内存示意图
共享内存数据结构
系统中可能会有大量的进程正在进行通信,因此系统当中就可能存在大量的共享内存,那么操作系统必然要对其进行管理(先描述,再组织)
所以共享内存除了开辟空间之外,系统还要为共享内存维护相关的内核数据结构。
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 */
};
当我们申请了一块共享内存后,由于有很多共享内存,所以为了让要实现通信的进程能够看到同一个共享内存,因此每一个共享内存被申请时都有一个key值来确定共享内存的唯一性
对于struct ipc_perm shm_perm;
,每个共享内存的key值存储在shm_perm
这个结构体变量当中
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;
};
共享内存函数
shmget函数
声明
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
返回值
- 成功,返回一个有效的共享内存标识符(用户层标识符)
- 失败,返回-1
参数
key_t key:
表示待创建共享内存在系统当中的唯一标识。一般调用ftok()
函数创建
key_t ftok(const char *pathname, int proj_id);
ftok函数是将一个已存在的路径名pathname
和一个整数标识符proj_id
t通过算法转换成一个key值,在使用shmget函数获取共享内存时,这个key值会被填充进维护共享内存的数据结构当中
size_t size:
表示待创建共享内存的大小,以字节为单位int shmflg:
表示创建共享内存的方式。
关于
shmflg
IPC_CREAT:
如果内核中不存在键值与key相等的共享内存,则新建一个共享内存并返回该共享内存;如果存在这样的共享内存,则直接返回该共享内存
IPC_CREAT | IPC_EXCL:
如果内核中不存在键值与key相等的共享内存,则新建一个共享内存并返回该共享内存;如果存在这样的共享内存,则返回出错
创建或获取共享内存,创建成功后,使用 shmat
将其和进程的地址空间关联。
shmat函数
声明
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
返回值
- 成功,返回一个指针,返回共享内存映射到进程地址空间中的起始地址
- 失败,返回-1
参数
int shmid:
表示待关联共享内存的用户级标识符。const void *shmaddr:
指定共享内存映射到进程地址空间的某一地址,通常设置为NULL,表示让内核自己决定一个合适的地址位置。int shmflg:
标志参数,可以包括一些特定的选项,比如 :SHM_RND和SHM_RDONLY
当不再需要共享内存时,需要使用 shmdt
将其去关联
shmdt函数
声明
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
返回值
- 成功,返回 0
- 失败,返回-1
参数
const void *shmaddr:
待去关联共享内存的起始地址,即调用shmat函数时得到的起始地址。
注意
将共享内存段与当前进程脱离不等于删除共享内存,只是取消了当前进程与该共享内存之间的联系。 所以,通常需要在不再需要共享内存时使用 shmctl
来删除共享内存段。
shmctl函数
声明
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
返回值
- 成功,返回一个非负整数,具体返回值取决于执行的命令
- 失败,返回-1
参数
int shmid:
由shmget返回的共享内存标识码int cmd:
将要采取的动作(有三个可取值)
IPC_STAT:
获取共享内存的状态,将信息写入buffer
参数指向的结构体中。
IPC_SET:
设置共享内存的状态,通过buffer
参数中提供的信息进行设置。
IPC_RMID:
从系统中删除共享内存段。
struct shmid_ds *buf :
指向一个保存着共享内存的模式状态和访问权限的数据结构
使用IPC_RMID
可以删除共享内存段。删除后,所有附加到该共享内存段的进程将不再能够访问共享内存,但直到所有进程都断开连接,系统才会回收该共享内存。(里面会有一个计数器来统计关联到该共享内存的进程个数)