Linux进程间通信 - 共享内存

news2024/10/7 4:17:02

之前的文章中我们讲述了匿名管道与命名管道相关的知识点,在本文中我们将继续讲述一种进程间通信的方式:共享内存。

systemV共享内存

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。

由于进程是相互独立的,那么进程间通信就跟我们之前所述的一样,需要让我们看到同一份的资源,这样才能够进行通信。

共享内存原理

我们知道创建一个进程就会产生一个PCB,地址空间以及页表结构,并且需要哪个将自己的数据映射到OS的物理内存中的特定区域,为了让不同的进程看到同一份资源,在物理内存中开辟一段空间,并将这段地址映射到进程A和进程B的共享区中,然后进程将映射的虚拟地址返回给用户,这样我们就完成了让不同的进程看到了同一份资源 -- 共享内存。当我们不需要使用共享内存的时候,就可以将共享内存的映射关系去掉 ,然后释放内存块。在宏观上控制共享内存,可以分为三个步骤:1、创建;2、关联或者取消关联;3、释放共享内存;

共享内存函数

shmget函数

功能:用来创建共享内存
原型
        int shmget(key_t key, size_t size, int shmflg);
参数
        key:这个共享内存段名字, 这个key我们通过使用ftok函数来获得

               key_t ftok(const char *pathname, int proj_id);
        size:共享内存大小
        shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

// IPC_CREAT and IPC_EXCL

// 单独使用IPC_CREAT:创建一个共享内存,如果共享内存不存在,就创建,如果已经存在就获取已经存在的共享内存并返回

// IPC_EXCL不能单独使用,一般要配合IPC_CREAT

// IPC_CREAT | IPC_EXCL:创建一个共享内存,如果共享内存不存在就创建,如果已经存在,则立马出错返回 -- 如果创建成功,对应的shm,一定是最新的!

shmat函数

功能:将共享内存段连接到进程地址空间
原型
        void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
        shmid: 共享内存标识
        shmaddr:指定连接的地址
        shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1

说明:

        shmaddr为NULL,核心自动选择一个地址
        shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
        shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
        shmflg=SHM_RDONLY,表示连接操作用来只读共享内存

shmdt函数

功能:将共享内存段与当前进程脱离
原型
        int shmdt(const void *shmaddr);
参数
        shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段

shmctl函数 

功能:用于控制共享内存
原型
        int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
        shmid:由shmget返回的共享内存标识码
        cmd:将要采取的动作(有三个可取值)
        buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

命令说明
IPC_STAT把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET把进程有足够权限的前提下,把共享内存的当前管来之设置为shmid_ds数据结构中给出的值
IPC_RMID删除共享内存段

简单的代码例子

系统中可以使用shm进行通信 -> 在任何一个时刻,可能有多个共享内存在被用来进行通信 -> 系统中一定会存在大量的shm同时存在 -> OS要对这些shm进行管理,使用之间学习的先描述再组织的方式将其管理起来。所以共享内存不仅仅只是在内存中开辟空间,系统为了管理shm,构建对应的描述共享内存的结构体对象 -> 共享内存 = 共享内存的内核数据结构 + 真正开辟的空间。 

创建key 

//comm.hpp
#define PATHNAME "."
#define PROJID 0x6666

const int gsize = 4096; //暂时

key_t getKey(){
    key_t k = ftok(PATHNAME, PROJID);
    if(k == -1){
        cerr << "error: " << errno << " : " << strerror(errno) << endl;
        exit(1);
    }
    return k;
}

string toHex(int x){
    char buffer[64];
    snprintf(buffer, sizeof buffer, "0x%x", x);
    return buffer;
}

//server.cc client.cc
// 1. 创建key
key_t k = getKey();
cout << "server key: " << toHex(k) << endl;

使用ftok函数对key的值进行创建,当不同的进程拥有了同样的pathname以及proj_id,那么就可以获得同一份共享内存的key值就可以找到同一份资源。

创建共享内存

在使用共享内存的时候一定是一个进程先创建,另一个进程获取。在创建的时候最好获得一个全新的。因此我们对shmget的flag选项设置成IPC_CREAT | IPC_EXCL


//comm.hpp
static int createShmHelper(key_t k, int size, int flag)
{
    int shmid = shmget(k, gsize, flag);
    if(shmid == -1)
    {
        cerr << "error: " << errno << " : " << strerror(errno) << endl;
        exit(2);
    }
    return shmid;
}

int createShm(key_t k, int size)
{
    umask(0);
    return createShmHelper(k, size, IPC_CREAT | IPC_EXCL | 0666); // 这里需要给共享内存添加权限,不然无法正常访问
}

int getShm(key_t k, int size)
{
    return createShmHelper(k, size, IPC_CREAT);
}


//server.cc client.cc
// 2. 创建共享内存
int shmid = createShm(k, gsize);
cout << "server shmid: " << shmid << endl;

运行结束时服务端与客户端退出了,但是共享内存并没有清理,再次运行就会发现出现了如下的情况,通过指令 ipcs -m 就可以查看对应的共享内存信息。

我们可以使用 ipcrm -m + [对应的shmid值] 进行删除,key值我们可以将其类比做文件的inode编号,shmid可以类比做文件的fd。对shm的未来的所有操作,在用户层都用shmid。

同样还有可以使用上述的shmctl函数进行删除操作,下面的图中就可以看到自动进行删除。

关联与去关联 

现在我们已经有了共享内存,但还并不能使用,需要将我们的进程与共享内存进行关联才能正常使用。共享内存的关联函数shmat与malloc函数的使用方法相似。

// comm.hpp
char* attachShm(int shmid){
    char *start = (char*)shmat(shmid, nullptr, 0);
    return start;
}

void detachShm(char *start){
    int n = shmdt(start);
    assert(n != -1);
    (void)n;
}

在ipcs -m 中还可以查看到 nattch 这个就可以看到对应的连接数,我们通过阅读上述的代码得知:先是无连接,然后是sever关联,再是cilent关联,最后是去关联。 

删除共享内存

在使用完成共享内存之后就可以将共享内存删除。

void delShm(int shmid)
{
    int n = shmctl(shmid, IPC_RMID, nullptr);
    assert(n != -1);
    (void)n;
}

通信 

在上述的工作都完成了之后就可以尝试开始进程间的通信:

在客户端依次写入大写的英文字母,在服务端对共享内存进行读取,从上面的图片中就可以看出。

一些其他的注意点

共享内存的大小

在创建共享内存的时候我们当时暂时设置了一个4KB的大小,当我们对其进行修改变成4097字节时可以发现查找共享内存是确实显示的是4097的大小,但当查看shmget函数的使用却发现:

它申请的内存是PAGE_SIZE的整数倍为单位的,而PAGE_SIZE的大小正好是4KB。由于申请了4097的空间大于4KB,操作系统在我们申请的时候会在底层给我们8KB,但是用户能够使用的只有4097大小的空间。

共享内存的速度

在通信的时候,没有使用任何的接口,因此一旦共享内存映射到进程的地址空间,该共享内存就直接被所有的进程直接看到了。因为共享内存的这种特性,可以让进程通信的时候减少拷贝次数,所以共享内存是所有进程间通信速度最快的。正常来说如果把外设到内存中的数据交互看做一次拷贝的话,那么通过管道通信的方式就需要至少四次拷贝,外设与内存需要两次,进程与管道之间也需要两次;而如果使用共享内存的话就只需要外设与内存间的两次拷贝,由于共享内存是物理地址通过页表映射到虚拟地址空间中的,就减少了拷贝的次数。

当我们只启动server时,已经创建了共享内存,但是此时若client并没有启动,server端就会无脑的进行读取,打印出来的全是空值,共享内存没有任何的保护机制(同步互斥)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/650178.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Downie 4 4.6.18 MAC上最新最好用的一款视频下载工具

Downie for Mac 简介 Downie是Mac下一个简单的下载管理器&#xff0c;可以让您快速将不同的视频网站上的视频下载并保存到电脑磁盘里然后使用您的默认媒体播放器观看它们。 Downie 4 下载 Downie 4 for Mac Downie 4 for Mac软件特点 支持许多站点 -当前支持1000多个不同的…

间接采购管理中常见的五大挑战

间接采购&#xff0c;有时也被称为间接费用或尾部支出&#xff0c;这些商品或服务不是制造产品直接必需的&#xff0c;而是日常运营所需的。 ● 办公室和行政用品 ● 商店地点的消耗品&#xff08;例如&#xff0c;清洁用品&#xff09; ● 设施管理费用 ● 专业服务 ● 旅行…

Linux 服务器重启之后执行指定脚本文件

有些时候&#xff0c;我们部署服务、中间件、数据库等应用的机器可能会因为机房停电、断电而宕机&#xff0c;这样大部分服务就随之关闭了&#xff0c;可能会需要手动去进行重启&#xff0c;我们可以通过 Linux 的开机启动来实现服务自动重启。 一、配置 /etc/rc.d/rc.local 文…

VOSviewer软件的基础与应用

VOSviewer是一款免费且专业的文献计量分析软件&#xff0c;也是一个知识图谱可视化工具&#xff0c;由荷兰莱顿大学开发&#xff0c;主要用于构建和查看文献计量知识图谱&#xff0c;基于文献的共引和共被引原理&#xff0c;具有可视化能力强、适合于大规模样本数据的特点&…

抖音矩阵系统源代码开发部署--源码搭建

抖音矩阵系统是一个具有强大功能的开放性平台&#xff0c;通过数据挖掘技术能够实现精准的用户画像和个性化推荐&#xff0c;这也是抖音成为国内最受欢迎的短视频平台之一的原因之一。矩阵系统的开发需要大量的技术支持和数据分析&#xff0c;同时也需要综合运用大数据、机器学…

Mysql主从复制及读写分离

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…

华为OD机试 JavaScript 实现【密码验证合格程序】【牛客练习题 HJ20】,附详细解题思路

一、题目描述 密码要求: 长度超过8位包括大小写字母.数字.其它符号,以上四种至少三种不能有长度大于2的包含公共元素的子串重复 &#xff08;注&#xff1a;其他符号不含空格或换行&#xff09; 二、输入描述 一组字符串。 三、输出描述 如果符合要求输出&#xff1a;OK&…

A stochastic parrot in every pot 每口锅里都会有一只随机鹦鹉? | 经济学人社论双语精翻

本期双语文章来自《经济学人》2023年5月13日周报社论&#xff1a;《人工智能经济学》&#xff08;The economics of AI&#xff09; A stochastic parrot in every pot? 每口锅里都会有一只随机鹦鹉&#xff1f; What does a leaked Google memo reveal about the future of A…

软文营销,如何写出优质的新闻稿

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 一&#xff0c;什么是软文营销&#xff1f; 软文营销是一种通过撰写和发布具有信息性、有趣性和可读性的文章&#xff0c;来推广产品、服务或品牌的营销策略。软文是指以一种较为隐晦、…

【默认端口】市面上各种中间件、软件、服务的默认端口汇总

常用软件&#xff0c;中间件&#xff0c;服务的默认端口汇总 常用软件默认端口汇总 市面上各种中间件、软件和服务的默认端口众多&#xff0c;下面列举一些常见的默认端口&#xff1a; SSH&#xff08;Secure Shell&#xff09;&#xff1a;22 Telnet&#xff1a;23 FTP…

AI在零售行业的应用

原创 | 文 BFT机器人 如今&#xff0c;零售商已经体验到使用人工智能 (AI) 的诸多好处&#xff0c;随着行业不断创新&#xff0c;人工智能的重要性只会越来越大。随着人工智能越来越被广泛接受&#xff0c;它的实施也越来越广泛。 查看这些用例&#xff0c;了解零售业中的 AI如…

一文掌握linux基本操作命令

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和…

Selenium元素定位的八种方法(建议收藏)

自动化一般需要四步操作&#xff1a;获取元素&#xff0c;操作元素&#xff0c;获取返回结果&#xff0c;断言&#xff08;返回结果与期望结果是否一致&#xff09;&#xff0c;最后自动出测试报告。Selenium提供8种元素定位的方法&#xff1a;id,name,class name,link text,xp…

三维点云机器学习检测定位圆心,三维圆检测,拟合轴线(基于open3d和python)

0.任务描述 背景&#xff1a;从端面拍摄大型圆筒工件&#xff0c;该工件周向尺寸大于相机视野&#xff0c;只能拍摄到1/3左右的圆周&#xff0c;且无法保证相机与端面垂直拍摄 任务&#xff1a;需要拟合圆周与轴线位置 难点&#xff1a;三维圆拟合与检测都很复杂&#xff0c…

fscan安装配置(windows、linux系统)

fscan安装配置(windows、linux系统) 1、简介 fscan一款内网综合扫描工具&#xff0c;方便一键自动化、全方位漏扫扫描。 它支持主机存活探测、端口扫描、常见服务的爆破、ms17010、redis批量写公钥、计划任务反弹shell、读取win网卡信息、web指纹识别、web漏洞扫描、netbios探…

程序员面试必备的 Java 八股文,适合所有的 Java 求职者

说明 本文分享 Java 后端真实高频面试题&#xff0c;有详细答案&#xff0c;保你稳过面试。题目包括&#xff1a;Java 基础、多线程、JVM、数据库、Redis、Shiro、Spring、SpringBoot、MyBatis、MQ、ELK、SpringCloud、设计模式等。 包含从简单到困难、从高频到低频的题目&…

EF Core中Partition by实现

一、SQL语句实现 Partition by是SQL Server数据库中提供的分区函数,跟Group by不同的是,Partition by能够按照分区返回所有记录,而Group by只能返回一条记录。 举个例子,有如下的数据库,需要找出每个唯一编号最新状态的数据。 显然,CW048201和CW048202它们的最新状态都…

靠着这套Github标星55K的Java面试笔记,成功拿到了2个大厂offer

作为一名优秀的程序员&#xff0c;技术面试是不可避免的一个环节&#xff0c;一般技术面试官都会通过自己的方式去考察程序员的技术功底与基础理论知识。 如果你参加过一些大厂面试&#xff0c;肯定会遇到一些这样的问题&#xff1a; 1、看你项目都用的框架&#xff0c;熟悉S…

Linux 环境下Docker将镜像打包导出到本地,上传至内网服务器(八)

文章目录 背景1. docker容器打包成镜像和压缩&#xff08;1&#xff09;首先查看镜像所在的容器&#xff0c;获取到容器id&#xff08;2&#xff09;将容器保存成镜像&#xff08;3&#xff09;将镜像打包&#xff08;4&#xff09;将镜像包压缩 2. docker镜像压缩包解压及镜像…

2023最新发布:Java 面试突击大全 带你摸熟 20+ 互联网公司面试考点

对于程序员来说&#xff0c;春招的失利意味着在金九银十要打一场“硬战”&#xff0c;可又有多少人做好了面试的准备呢&#xff1f;对于一线互联网公司的面试&#xff0c;你又了解多少呢&#xff1f; 今天&#xff0c;一本《Java 面试考点大全》全网首发&#xff0c;带你摸熟 …