前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >进程通信(三)共享内存

进程通信(三)共享内存

作者头像
lexingsen
发布2022-02-24 15:26:54
1.2K0
发布2022-02-24 15:26:54
举报
文章被收录于专栏:乐行僧的博客乐行僧的博客

一、共享内存

共享内存是操作系统直接在物理内存上开辟一段空间作为进程间通信的缓冲区域, 与管道、消息队列等其他进程通信方式相比较,共享内存拥有更高的效率,原因是共享内存的设计是基于物理内存的地址直接进行操作的,这样相比其他方式的IPC省去了重重的系统调用,因此在很大程度上提高了其效率。

二、共享内存原理

在这里插入图片描述
在这里插入图片描述

当不同进程的虚拟地址空间按照页面的大小加载到内存时,CPU发出的虚拟地址经过MMU(内存管理单元)的地址转换之后,就可以得到对应的物理地址,然后就可以进行访存等一系列操作,而共享内存API就是让不同的进程虚拟地址空间的某部分经过加载然后映射到的是同一块物理内存上,这样就可以使得不同的进程访问同一块物理内存,从而进行不同进程间的进程通信。需要注意的是:由于此时这块用于进程通信的物理内存成为临界区,因此当不同的进程访问时,可能会出现竞态条件,因此为了实现进程同步,当不同进程访问临区时,必须对进程实现互斥和同步操作。这点非常重要,希望能引起重视。

三、共享内存API分析

代码语言:javascript
复制
shm:share memory共享内存的简写
1.shmget
函数原型:int shmget(key_t key, size_t size, int shmflg);
函数功能:创建新的或者已有的共享内存。
参数key:key值,与消息队列所需要的key是一样的,如何得到?
请看:(https://blog.csdn.net/ASJBFJSB/article/details/104122200)
(1)如果key值没有对应的共享内存,创建出来一个新的共享内存,创建的过程实质上就是os在物理内存
上划分出一块区域作为贡献内存的存储空间。
(2)如果key值已经确定,说明已经有一个共享内存了,是由之前其他进程创建的,此时shmget就是获取
该key对应的共享内存。

参数size:指定共享内存的大小,一般是指定的是页面大小的整数倍(4k),如果不是,为了方便映射以及内存
管理,api会自动将size调整为页面大小的整数倍。

参数shmflg:指定权限以及IPC_CREAT。

返回值:
成功:返回共享内存的唯一标识符。
失败:返回-1。
代码语言:javascript
复制
2.shmat
函数原型:void *shmat(int shmid, const void *shmaddr, int shmflg);

功能:将shmid指向的共享内存的空间映射到当前进程的虚拟地址空间上,
并且返回映射后的起始地址。得到这个地址,就可以在此地址上进行读写操作。

参数shmid:shmid是由shmget得到标识共享内存的唯一标识符。
参数shmaddr:指定映射的起始地址。
(1)自己设置映射的起始地址(虚拟地址空间的地址),建议不要使用,因为你是搞不懂在虚拟地址空间中
哪里用了,哪里可能还没有使用。
(2)传NULL,由操作系统填写映射的地址。因为只有os才知道哪里能够映射,哪里不能映射。
参数shmflg:指定映射条件。
(1)0:以可读可写的方式进行共享内存的映射。
(2)SHM_RDONLY:以只读的方式进行映射。

返回值:
成功:返回映射地址
失败:返回(void*)-1
代码语言:javascript
复制
3.shmdt
函数原型:int shmdt(const void *shmaddr);
功能:断开当前进程虚拟地址空间与shmaddr指向的共享内存的映射。
参数shmaddr:指向共享内存的指针
需要注意的是shmdt并不能进行共享内存区域的删除。需要使用shmctl或者ipcrm进行删除。
代码语言:javascript
复制
4.shmctl
函数原型:int shmctl(int shmid, int cmd, struct shmid_ds* buf);
函数功能:与消息队列的msgctl功能几乎一样,shmctl也提供了三个命令。
IPC_RMID:删除由shmid唯一标识的共享内存区。此时第三个参数不使用,传入NULL。
IPC_SET:设置shmid_ds中的三个成员,分别是:shm_perm.uid,shm_perm.gid和shm_perm.mode。
IPC_STAT:buf作为传出参数,获取shmid唯一标识的共享内存的shmid_ds结构。

四、共享内存实战代码

代码语言:javascript
复制
// 发送端进程
#include <stdio.h>
#include <assert.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#define PAGESIZE 4096

int main() {
	int shmid = shmget((key_t) 123, PAGESIZE, 0664|IPC_CREAT);
	assert(shmid != -1);
	
	char * p = (char*)shmat(shmid, NULL, 0);
	assert(p != (char*)-1);
	while (1) {
		printf("input: ");
		fgets(p, PAGESIZE-1, stdin);
		if (strncmp(p, "end", 3) == 0) break;
	}
	shmdt(p);
	return 0;
}


//接收端进程
#include <stdio.h>
#include <assert.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#define PAGESIZE 4096

int main() {
	int shmid = shmget((key_t) 123, PAGESIZE, 0664|IPC_CREAT);
	assert(shmid != -1);
	
	char * p = (char*)shmat(shmid, NULL, 0);
	assert(p != (char*)-1);
	while (1) {
		if (strncmp(p, "end", 3) == 0) break;
		printf("%s ", p);
		sleep(1);
	}
	shmdt(p);
	return 0;
}

五、进程安全的探讨

在上述的代码中,由于未对共享内存临界区做不同进程互斥的操作,当操作系统分配给进程的CPU时间片使用完成后,可能会出现当前进程还未完成任务,而另外的进程已经从共享内存中拿走了未完成任务的数据,这样的问题在进程通信中是非常致命的,那么如何解决进程在共享内存的通信过程中实现数据的同步和互斥,避免数据发生混乱的情况呢,请看下篇博客,信号量的使用。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
消息队列 CMQ
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档