Linux通信--构建进程通信IPC的方案之共享内存|实现使用共享内存进行serverclient通信

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

目录

一、共享内存的原理

二、使用共享内存

三、共享内存函数

1.shmget(用来创建共享内存)

2.shmat(将共享内存和进程地址空间关联)

3.shmctl(用于控制共享内存)

4.shmdt(将共享内存段与当前进程脱离)

四、共享内存server&client通信测试

①创建共享内存

 ②关联共享内存和进程

//2-3 进行通信

③取消关联进程和共享内存

④关闭共享内存


一、共享内存的原理

  • 1.在物理内存中开辟一块空间
  • 2.让不同的进程通过页表将该空间映射到自己的进程虚拟地址空间中
  • 3.不同进程通过操作自己进程虚拟空间中的虚拟地址,来操作共享内存。

进程A和B都通过各自的页表将自己的虚拟地址和物理地址进行对应,进程A操作虚拟地址写入数据,保存在物理内存,进程B读取该物理内存。

二、使用共享内存

共享内存在物理地址空间上,是在共享区中。

  1. 创建共享内存
  2. 关联进程--将进程的虚拟地址和共享内存的地址通过页表建立映射关系
  3. 通信
  4. 取消关联进程--通过将进程中页表的key-value删除
  5. 共享内存释放

三、共享内存函数

上图表示进程A和进程B可以通过共享内存来通信,同样C和D也可以通过另一块共享内存通信。所以在系统中,一定同时存在多个共享内存,os需要对这些内存块做管理,所以使用一个结构体,里面存放共享内存的各个属性

所以 共享内存 == 共享内存的内核数据结构 + 真正开辟的物理空间

1.shmget(用来创建共享内存)

原型:int shmget(key_t key,size_t size,int shmflg)

参数:key 这个共享内存段名字 size 共享内存大小 shmflg 由九个权限标志构成,用法的mode一样

返回值:成功返回一个非负整数,即共享内存段的标识码,失败返回-1

其中key_t key 是由ftok算法生成的随机值,具有唯一性,ftok函数如下:

key_t  ftok(const char * pathname,int proj_id)

形参:pathname 传入一个地址  proj_id 输入一个数字 这两个值可以任意设置,但是在通信双方必须是相同的,具体地,A进程调用ftok生成key,将key放入struct shm中,B也生成相同的key,B用这个key去struct shm去匹配,匹配成功,就找到了共享内存。

shmflg宏含义
IPC_CREAT单独使用时,创建一个共享内存,如果不存在就直接创建,如果存在就获取已有的共享内存起始地址返回
IPC_EXCL需要依赖IPC_CREAT使用,如果不存在则创建共享内存,如果存在立马退出报错返回

2.shmat(将共享内存和进程地址空间关联)

原型:void * shmat(int shmid,const void * shmaddr,int shmflg);

参数: shmid 共享内存标识 shmaddr 指定连接的地址 shmflg它的两个可能取值是SHM_RND 和SHM_RDONLY

返回值:成功返回一个指针,指向共享内存的起始地址,失败返回-1(类似于malloc)

说明:shmaddr为null,os自动选择一个地址,

           shmaddr不为null,且shmflg无SHM_RND标记,则以shmaddr为连接地址

         shmaddr不为null,且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整 shmlba的整数倍

            shmflg = SHM_RDONLY 表示连接操作用来只读共享内存

3.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删除共享内存段


4.shmdt(将共享内存段与当前进程脱离)

原型:int shmdt(const void * shmaddr);

参数:shmaddr:由shmat所返回的指针

成功返回0,失败返回-1

注:将共享内存与当前进程脱离不等于删除共享内存段 

四、共享内存server&client通信测试

使用共享内存通信的步骤:创建共享内存,关联进程和共享内存,进行通信,取消关联,删除共享内存

 创建4个文件:server.cc,client.cc,common.hpp,makefile。server实现写入,client实现读取

makefile要生成两个目标文件server,client,可以这样写:

.PHONY:all
all:server client
client:client.c comm.cgcc -o $@ $^
server:server.c comm.cgcc -o $@ $^
.PHONY:clean
clean:rm -f client server

common.hpp中声明和定义一些客户端和服务端公有的函数,如下:

这个文件中主要实现创建共享内存,关联进程和取消关联,共享内存释放。

①创建共享内存

  1. 首先,创建共享内存,使用shmget
  2. 其中有一个key,使用ftok生成,在两端调用即可\
  3. 通信的时候,一个进程创建共享内存,一个进程获取.
  4.   共享内存创建时,是按字节为单位。
//创建共享内存,要知道k和这个共享内存的大小
int createShm(key_t k, int size)
{//创建的时候,最好创建全新的int shmid = shmget(k,gsize,IPC_CREAT|IPC_EXCL);if(shmid == -1){//创建失败exit(2);}return shmid;
}//创建成功,一个进程获取
int getShm(key_t,int size)
{int shmid = shmget(k,gsize,IPC_CREAT);return shmid;
}

从上述两个函数,一个创建一个获取,只是shmflg不同,所以可以封装为一个函数

//static 只在本文件内有效
static int createShmHelper(int k, int size,int flag)
{int shmid = shmget(k,gsize,flag);if(shmid == -1){exit(2);}return shmid;
}int createShm(key_t k,int size)
{//创建的时候注意加权限umask(0);return createShmHelper(key,gsize,IPC_CREAT|IPC_EXCL|0666);
}int getShm(key_t k,int size)
{return createShmHelper(key,gsize,IPC_CREAT);
}

 ②关联共享内存和进程

        在物理内存中创建好共享内存后,这个共享内存在创建的时候必须有权限才能进行操作。关联共享内存和进程,将共享内存的起始地址经过页表映射放到进程pcb中,具体挂接到pcb的哪里可以自己设定,设置为null让系统自主选择,即完成了关联。

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

//2-3 进行通信

具体地通信可以自己设置

③取消关联进程和共享内存

detach(char * start)
{int n = shmdt(start);(void)n;
}

④删除共享内存

当运行两个端,发现进程退出后,再次运行无法创建共享内存,说明共享内存没有直接随着进程关闭。

关闭共享内存可以用两种方法

  1. ipcrm - m命令
  2. 函数shmctl
void delShm(int shmid)
{int n =  shmctl(shmid,IPC_RMID,nullptr);assert(n != -1);(void)n;
}

server.cc中主要实现创建共享内存,关联共享内存,进行通信(从共享内存读数据),取消关联,删除共享内存。

client.cc中主要实现获取共享内存,关联共享内存,进行通信(写数据到共享内存),取消关联。

接下来,优雅的修改上面的代码,封装起来。

common.hpp:

//前面的方法不变#define SERVER 1
#define CLIENT 0class Init
{public://构造Init(int t):type(t){key_t k = getKey();if(type == SERVER) shmid = createShm(k, gsize);elseshmid = getShm(k, gsize);start = attachShm(shmid);}char *getStart(){     return start;}//析构~Init(){detachShm(start);if(type == SERVER) delShm(shmid);}
private:char *start;int type;     //server or clientint shmid;
};

server.cc

int main()
{Init init(SERVER);char * start = init.getStart();//开始通信....//读取int n = 0;while(n <= 26){cout<<" "<<start<<endl;  //设置start里都是字符串sleep(1);}//因为init是一个临时对象,所以函数跑完会自动调用析构return 0;
}

client.cc

int main()
{Init init(CLIENT);char * start = init.getStart();//开始通信...//往start里写char c = 'A';while(c <= 'Z'){start[c-'A'] = c;c++;start[c] = '\0';sleep(1);}return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

研华I/O板卡 Win10+Qt+Cmake 开发环境搭建

文章目录 一.研华I/O板卡 Win10QtCmake 开发环境搭建 一.研华I/O板卡 Win10QtCmake 开发环境搭建 参考这个链接安装研华I/O板卡驱动程序系统环境变量添加研华板卡dll Qt新建一个c项目 cmakeList.txt中添加研华库文件 cmake_minimum_required(VERSION 3.5)project(advantechDA…

基于爬行动物算法优化的BP神经网络(预测应用) - 附代码

基于爬行动物算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于爬行动物算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.爬行动物优化BP神经网络2.1 BP神经网络参数设置2.2 爬行动物算法应用 4.测试结果&#xff1a;5…

【C++】—— C++11之可变参数模板

前言&#xff1a; 在C语言中&#xff0c;我们谈论了有关可变参数的相关知识。在C11中引入了一个新特性---即可变参数模板。本期&#xff0c;我们将要介绍的就是有关可变参数模板的相关知识&#xff01;&#xff01;&#xff01; 目录 序言 &#xff08;一&#xff09;可变参…

kafka架构和原理详解

Apache Kafka 是一个分布式流数据平台,用于高吞吐量、持久性、可扩展的发布和订阅消息。它具有高度的可靠性,被广泛用于构建实时数据流处理、日志收集和数据管道等应用。 基本架构 1. 主题(Topic): 主题是消息的逻辑分类生产者将消息发布到特定的主题中,而消费者可以订阅…

gitlab提交项目Log in with Access Token错误

目录 报错信息 问题描述 解决方案 报错信息 问题描述 在提交项目到gitlab时&#xff0c;需要添加账户信息 &#xff0c;但是报了这样一个错&#xff0c;原因应该就是路径问题&#xff0c;我在填写server地址的时候&#xff0c;就出现了路径问题&#xff0c;我把多余的几个/…

远传水表数据是怎么远传的?

随着科技的不断发展&#xff0c;智慧城市的建设逐渐成为城市发展的重要方向&#xff0c;而智能水表作为智慧城市中的重要组成部分&#xff0c;它的数据远传功能更是给水务管理带来了极大的便利。下面就由在智能水电表行业摸爬滚打多年的小编来为大家讲解下吧! 一、远传水表数据…

在 Spring Boot 中集成 MinIO 对象存储

MinIO 是一个开源的对象存储服务器&#xff0c;专注于高性能、分布式和兼容S3 API的存储解决方案。本文将介绍如何在 Spring Boot 应用程序中集成 MinIO&#xff0c;以便您可以轻松地将对象存储集成到您的应用中。 安装minio 拉取 minio Docker镜像 docker pull minio/minio创…

正交实验如何进行方差分析?

一、案例介绍 欲研究温度&#xff08;℃&#xff09;、时间&#xff08;min&#xff09;、催化剂含量&#xff08;%&#xff09;对某物质转化率的影响&#xff0c;当前使用正交试验设计进行试验&#xff0c;试探究3个因素对转化率的影响是否显著&#xff0c;并找到能使转化率达…

精读《算法题 - 地下城游戏》

今天我们看一道 leetcode hard 难度题目&#xff1a;地下城游戏。 恶魔们抓住了公主并将她关在了地下城 dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里&#xff0c;他必须穿过地下城并通过对抗恶魔来拯救公主。 骑士…

设计模式之命令模式(Command)的C++实现

1、命令模式的提出 在软件开发过程中&#xff0c;“行为请求者”和“行为实现者”通常呈现一种“紧耦合”&#xff0c;如果行为的实现经常变化&#xff0c;则不利于代码的维护。命令模式可以将行为的请求者和行为的实现者进行解耦。具体流程是将行为请求者封装成一个对象&…

vulhub之MinIO信息泄露漏洞(CVE-2023-28432)

文章目录 0x01 前言0x02 漏洞描述0x03 影响范围0x04 漏洞复现1.启动环境2.查看端口3.构造POC 0x05 修复建议 0x01 前言 本次测试仅供学习使用&#xff0c;如若非法他用&#xff0c;与本文作者无关&#xff0c;需自行负责&#xff01;&#xff01;&#xff01; 0x02 漏洞描述 …

多线程应用——单例模式

单例模式 文章目录 单例模式一.什么是单例模式二.如何实现1.口头实现2.利用语法特性 三.实现方式&#xff08;饿汉式懒汉式&#xff09;1.饿汉式2.懒汉式3.线程安全的单例模式4.双重检查锁5.禁止指令重排序 一.什么是单例模式 单例模式&#xff08;Singleton Pattern&#xff…