System V 共享内存
通信原理
- 多个进程共享物理内存的同一块区域(通常被称为“段” :segment)
- 抛弃了内核“代理人”角色,让两个进程直接通过一块内存通信
System V 共享内存
通信原理
- 多个进程共享物理内存的同一块区域(通常被称为“段” :segment)
- 抛弃了内核“代理人”角色,让两个进程直接通过一块内存通信
共享内存 VS 消息队列/管道
优势
- 减少了内存拷贝(从用户空间拷贝到内核、从内核拷贝到用户空间)
- 减少了2次系统调用,提高了系统性能
使用System V 共享内存
操作流程
- 获取共享内存对象的ID
- 将共享内存映射至本进程虚拟空间的某个区域
- 不同进程通过对这块共享内存进行读写、传输数据
- 当进程不再使用这块共享内存时,解除映射关系
- 当没有进程再需要这块共享内存时,删除它
使用共享内存
相关API
- 获取共享内存对象的ID:int shmget(key_t key, size_t size, int shmflg);
- 映射共享内存:void *shmat(int shmid, const void *shmaddr, int shmflg);
- 解除内存映射:int shmdt(const void *shmaddr);
- 设置内存对象:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
- 查看IPC对象信息:ipcs -m
相关API
获取共享内存对象的ID
shmget函数负责创建或打开共享内存段
- 函数原型:int shmget(key_t key, size_t size, int shmflg);
- 函数功能:创建或打开一个共享内存对象
- 函数参数:
- key:IPC对象的键值,一般为IPC_PRIVATE或ftok返回的key值
- Size:共享内存大小,一般为内存物理页的整数倍
- shmflg:
- IPC_CREAT:如果不存在与指定的key对应的段,那么就创建一个新段
- IPC_EXCL:若key指定的内存存在且指定了IPC_CREAT,返回EEXIST错误
- SHM_HUGETLB:使用巨页(huge page)
- 返回值:共享内存的标识符ID
attach共享内存——使用共享内存
shmget函数,不过是在茫茫内存中创建了或找到了一块共享内存区域,但是这块内存和进程尚没有任何关系。要
想使用该共享内存,必须先把共享内存引入进程的地址空间,这就是attach操作。
- 函数原型:void *shmat(int shmid, const void *shmaddr, int shmflg);
- 函数功能:将shmid标识的共享内存引入到当前进程的虚拟地址空间
- 函数参数
- 返回值:共享内存段的地址
- shmat如果调用成功,则返回进程虚拟地址空间内的一个地址。如果失败,就会返回(void*)-1
detach共享内存——分离共享内存
shmdt函数仅仅是使进程和共享内存脱离关系,并未删除共享内存。shmdt函数的作用是将共享内存的引用计数减1。如前所述,只有共享内存的引用计数为0时,调用shmctl函数的IPC_RMID命令才会真正地删除共享内存。
- 函数原型:int shmdt(const void *shmaddr);
- 函数功能:解除内存映射,将共享内存分离出当前进程的地址空间
- 函数参数:
- TIPS:
- 通过fork创建的子进程会继承父进程所附加的共享内存段,父子进程可以通过共享内存进行IPC通信。
- 在exec系统调用中,所有附加的共享内存段都会被分离
- 函数shmdt仅仅是使进程和共享内存脱离关系,将共享内存的引用计数减1,并未删除共享内存。
- 当共享内存的引用计数为0时,调用shmctl的IPC_RMID命令才会删除共享内存
设置共享内存属性
- 函数原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
- 函数功能:获取/设置共享内存对象属性
- 当cmd为IPC_STAT和IPC_SET时,需要用到第三个参数
- 函数参数:
- shmid:共享内存对象ID
- cmd:
- 当cmd为IPC_STAT和IPC_SET时,需要用到第三个参数
- IPC_STAT: 用于获取shmid对应的共享内存的信息。
- IPC_SET:IPC_SET也只能修改shm_perm中的uid、gid及mode
- IPC_RMID:可以通过如下方式删除共享内存段:如果共享内存的引用计数shm_nattch等于0,则可以立即删除共享内存。
- buf:
- 将该内存对象关联的shmid_ds数据结构拷贝到参数buf中
编程方式
- 创建
- 映射
- 读写
- 解除映射
- 销毁
共享内存share memory写端
int main(int argc, char *argv[])
{
key_t key = ftok(".", 588);
int shm_id = shmget(key, 4096, IPC_CREAT|0666 );
printf("shm_id:%d\n", shm_id);
/* Automatic allocation */
char *shm_p = shmat(shm_id, NULL, 0);
memset(shm_p, 0, sizeof(shm_p));
fgets(shm_p, 4096, stdin);
sleep(30);
shmctl(shm_id, IPC_RMID, NULL);
return 0;
}
共享内存share memory读端
int main(int argc, char *argv[])
{
key_t key = ftok(".", 588);
int shm_id= shmget(key, 4096, 0666);
char *shm_p = shmat(shm_id, NULL, 0);
printf("from share memory:%s\n", shm_p);
shmdt(shm_p);
return 0;
}
共享内存的通信限制
- SHMMNI:系统所能创建的共享内存的最大个数,IPCMIN:32768
- SHMMIN:一个共享内存段的最小字节数4096
- SHMMAX:一个共享内存段的最大字节数33554432
- SHMALL:系统中共享内存的分页总数2097152
- SHMSEG:一个进程允许attch的共享内存段的大小个数
共享内存通信特点
- 共享内存抛弃了“内核代理人”角色,提升了系统性能
- 需要进程本身维护共享内存的各种问题:同步、互斥....
- 一般需要信号量、互斥锁、文件锁等配合使用,在各个进程之间在高效通信的同时,防止发送数据的践踏,破坏
|