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.c
 gcc -o $@ $^
server:server.c comm.c
 gcc -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 0

class Init
{

public:
    //构造
    Init(int t):type(t)
    {
        key_t k = getKey();
        if(type == SERVER) 
            shmid = createShm(k, gsize);
        else
             shmid = 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 client
    int 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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【kubernetes系列】Calico原理及配置

概述 Calico是针对容器&#xff0c;虚拟机和基于主机的本机工作负载的开源网络和网络安全解决方案。 Calico支持广泛的平台&#xff0c;包括Kubernetes&#xff0c;OpenShift&#xff0c;Docker EE&#xff0c;OpenStack和裸机服务。 Calico在每个计算节点都利用Linux Kernel实…

七牛云OSS存储

前言: 七牛云的存储项目的附件,需要开发一套七牛云的工具类,可以使用该工具类进行七牛云服务器进行文件的上传与下载操作; 七牛云的文档学习: 相关的依赖项的配置: <dependency><groupId>com.amazonaws</groupId><artifactId>aws-java-sdk-s3…

C#关于WebService中File.Exists()处理远程路径的异常记录

目录 前言方案一打开网站对应的程序池的高级设置按下图步骤设置凭据重启网站若方案一未能解决&#xff0c;请继续尝试方案二&#x1f447; 方案二从控制面板进入到 凭据管理器为windows凭据添加凭据点击**Windows凭据**&#xff0c;并点击**添加Windows凭据**键入远程路径的地址…

使用c的标准库函数创建线程

#include <stdio.h> #include <threads.h> #include <time.h>int thrd_proc(void * varg){// 打印10次int times 10;struct timespec ts {1,0}; // 1秒, 0纳秒while(times--){printf("%s\n",(char *)varg);// 每隔1秒,打印一次thrd_sleep(&t…

Revit SDK: ProximityDetection_WallJoinControl 墙相交检测

前言 这个例子介绍了如何使用过滤器以及墙体的位置线来及进行相交检测&#xff0c;包括&#xff1a; 找到和墙相交的柱子找到出入口的障碍物找到墙各个端点接近的墙验证墙和其它墙的交接情况 内容 找到和墙相交的柱子 关键点在于 ElementIntersectsElementFilter 可以用于…

配置环境变量的作用

配置环境变量的作用 一般运行过程&#xff1a;寻找QQ.exe所在的目录&#xff0c;输入QQ.exe配置环境变量&#xff1a;把QQ所在的路径配给操作系统Path&#xff0c; 在任何路径下都能运行QQ.exe 举例&#xff1a; 定义变量&#xff1a;SCALA_HOME SCALA_HOME、JAVA_HOME 等这…

Python面试:什么是GIL

1. GIL (Global Interpreter lock)可以避免多个线程同时执行字节码。 import threadinglock threading.Lock()n [0]def foo():with lock:n[0] n[0] 1n[0] n[0] 1threads [] for i in range(5000):t threading.Thread(targetfoo)threads.append(t)for t in threads:t.s…

机器连接和边缘计算

以一种高效、可扩展的方式进行连接和边缘计算的结合&#xff0c;解决了在工业物联网应用中的机器数据集成问题。 一 边缘计算 边缘计算描述了由中央平台管理的数据分散式处理&#xff0c;它对于工业物联网而言非常重要。在许多应用程序中&#xff0c;由于数据量非常大&#xf…

修改linux中tomcat的端口

随便修改一个 以8055为例子 开放8081端口 firewall-cmd --permanent --add-port8081/tcp firewall-cmd --reload firewall-cmd --list-all

Java 并发编程

Java 并发编程 一、线程创建1.继承 Thread 类2.实现 Runnable 接口3.实现 Callable 接口 二、线程方法三、线程同步1.同步代码块2.同步方法3.ReentrantLock4.乐观锁 四、线程池1.ThreadPoolExecutor2.Executors 一、线程创建 1.继承 Thread 类 通过继承 Thread 类来创建线程是…

【高性能计算】opencl语法及相关概念(三)事件,内存

opencl中的事件概念 当谈到OpenCL中的事件时&#xff0c;它们代表了执行的各个阶段或操作的状态信息。通过使用事件&#xff0c;您可以跟踪和管理内核执行以及内存操作的进度和顺序。以下是与OpenCL事件相关的关键概念&#xff1a; 创建事件&#xff1a;您可以使用clCreateUse…

Java运行时jar时终端输出的中文日志是乱码

运行Jar时在控制台输出的中文日志全是乱码&#xff0c;这是因为cmd/bash默认的编码是GBK&#xff0c;只要把cmd的编码改成UTF-8即可 两种方式修改&#xff1a;临时修改和注册表永久修改 临时修改 只对当前的cmd页面有效&#xff0c;关闭后重新打开都会恢复成GBK, 打开cmd&am…

记一次对链接、COMMON块、多重符号定义的理解

问题引入 首先是两个测试程序 // foo.c long long int a;// bar.c #include <stdio.h>int a; int main(){a 1;long long int len sizeof(a);printf("%lld\n", len);return 0; }将两个程序链接到一起 问题&#xff1a;len等于几&#xff1f; 初步分析 环境…

研磨设计模式day15策略模式

场景 问题描述 经常会有这样的需要&#xff0c;在不同的时候&#xff0c;要使用不同的计算方式。 解决方案 策略模式 定义&#xff1a; 解决思路&#xff1a;

PyCharm软件安装包分享(附安装教程)

目录 一、软件简介 二、软件下载 一、软件简介 PyCharm是一种集成开发环境&#xff08;IDE&#xff09;&#xff0c;专门为Python开发者设计。它是由捷克软件公司JetBrains开发的&#xff0c;为Python开发人员提供了高效、易用和功能丰富的工具集。 以下是PyCharm软件的主要…

基于 Docker 的 MySQL 主从复制搭建(Mac M1版本)

系统&#xff1a;Macbook M1 镜像版本&#xff1a;mysql:5.7 如果是要查 slave连接不上 master的问题&#xff0c;可以直接跳到文章末尾踩坑处 准备工作 拉取镜像 docker pull mysql:5.7本地数据卷挂载 因为mysql不挂载的话&#xff0c;重启丢失数据&#xff0c;所以在本地创…

用Cmake build OpenCV后,在VS中查看OpenCV源码的方法(环境VS2022+openCV4.8.0) Part III

用Cmake build OpenCV后&#xff0c;在VS中查看OpenCV源码的方法(环境VS2022openCV4.8.0) Part III 用Cmake build OpenCV后&#xff0c;在VS中查看OpenCV源码的方法&#xff08;环境VS2022openCV4.8.0&#xff09; Part I_松下J27的博客-CSDN博客 用Cmake build OpenCV后&…

Maven的profiles多环境配置

一个项目通常都会有多个不同的运行环境&#xff0c;例如开发环境&#xff0c;测试环境、生产环境等。而不同环境的构建过程很可能是不同的&#xff0c;例如数据源配置、插件、以及依赖的版本等。每次将项目部署到不同的环境时&#xff0c;都需要修改相应的配置&#xff0c;这样…

学习node之——如何在项目中使用MySQL、前后端的身份认证

上一篇文章只写了一丢丢&#xff0c;这篇才是正片&#xff0c;look look look 一、使用mysql模块操作数据库 1、查询数据 这里连接数据库的用户和密码都是我们在安装mysql时配置的密码。每个人的users表格里面数据不同&#xff0c;结果也会不一样哟&#xff01; // 导入mys…

统一网关Gateway

文章目录 概览网关的作用搭建网关断言工厂路由过滤器全局过滤器案例 过滤器执行顺序跨域问题 概览 网关的作用 搭建网关 断言工厂 路由过滤器 全局过滤器 案例 过滤器执行顺序 跨域问题