【进程间通信】共享内存

文章目录

  • 共享内存
    • 常用的接口
    • 指令
    • 利用命名管道实现同步机制
    • 总结

System V的IPC资源的生命周期都是随内核的。

共享内存

共享内存也是为了进程间进行通信的,因为进程间具有独立性,通信的本质是两个不同的进程看到同一份公共资源,所以共享内存一定是由OS提供的,共享内存的本质就是,在内存中开辟一块空间,然后映射在需要通过这片空间的进程的地空间中堆和栈之间共享区,通过这样的方式让两个不同的进程看到同一份公共资源。如下图:
在这里插入图片描述
一个共享内存可以满足很多进程进行通信,它可以挂载很多进程,但是OS中的进程是很多的,不同的进程和不同的进程之间需要通信的内容和方式也不太一样,所以OS中一定会存在大量的共享内存,因此OS一定要对共享内存进行管理(先描述,在组织),所以OS中一定有描述共享内存的各种属性的结构体,然后用链表等数据结构把OS中存在的共享内存管理起来,这样对共享内存的管理就变成了对链表的增删查改。
在这里插入图片描述

那么如何保证两个不同进程能看到同一个共享内存呢?
OS是让两个进程通过约定的方式让不同的进程看到同一块共享内存,两个进程约定一个key值,这个key值会被写入shm结构体中,然后不同的进程拿着相同的key值,就可以看到同一份共享内存了,但是系统中的共享内存会存在很多个,这个key值每个共享内存应该是不同的,所以我们需要尽可能的保证key的唯一性,但是让用户来进行这个操作,明显是不靠谱的,所以有个系统调用的函数,可以来帮助我们形成这个key值。
在这里插入图片描述
只需要传进去一个字符串和一个整型变量,这个函数就会通过自身的算法给我们形成一个key值,两个不同的进程拿着相同的字符串和相同的整型变量再通过相同的算法,形成的key值一定是一样的。这个key值最终是要被写进内核数据结构的。
在这里插入图片描述
这个__key就是我们用户自己传进去的key。共享内存 = 共享内存空间 + 共享内存属性

常用的接口

shmget
在这里插入图片描述
第一个参数就是我们之前自己形成的key,最后一个最常用的标志位就两个,IPC_CREAT和IPC_EXCL

  1. IPC_CREAT :不存在就创建,存在就获取
  2. IPC_EXCL:单独使用没有任何意义,和IPC_CREAT 一起使用,不存在就创建,存在就报错,这两个参数一起使用可以保证一定是第一次创建共享内存。

第三个参数的标志位后面可以直接跟共享内存的权限,具体可以参考下面的代码。

它的返回值就类似于文件描述符,方便用户操作,并且后面的节后都需要用这个shmid来标识自己创建的共享内存,我们的key是内核里面用的为了标识共享内存的唯一性,我们用户用的都是shmid,并且指令删除共享内存也是要用shmid不能使用key。

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

创建成功后,我们会拿到一个指针,这个指针就和malloc开辟的空间一样,可以直接拿着指针对这块内存进行操作。第二个参数我们一般为nullptr就行,最后一个参数一般为0。

shmdt
在这里插入图片描述

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

指令

常用相关的函数就是这些,在Linux中如果我们要查看已经创建的共享内存有哪些,可以使用ipcs -m 指令
在这里插入图片描述
想要删除一个共享内存可以使用ipcrm -m shmid 的方式删除
在这里插入图片描述
最后的数字应该为具体的shmid。

利用命名管道实现同步机制

共享内存不会提供同步机制,但是他是需要同步机制的,我们可以使用管道来实现它的同步机制,接下来我们可以利用前面的命名管道尝试实现一下这个同步机制。

我们需要两个独立的进程一个server,一个client,所以我们需要一个可以形成两个可执行程序的Makefile。

.PHONY:all
all:client server

client:Client.cc
	g++ -o $@ $^ -std=c++11

server:Server.cc
	g++ -o $@ $^ -std=c++11

.PHONY:clean
clean:
	rm -rf client server

然后我们需要一个Client.cc和Server.cc,但是我们需要形成同一个key,所以我们可以把两个源文件中需要两个进程看到的同一份资源抽出来,Com.hpp。

Com.hpp

#pragma once
#include <iostream>
#include <string>
#include <cerrno>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

const std::string path = "/home/ysl/long/class_dragon/test_5_8/shm";
const std::string filename = "fifo";
const int proj_id = 0x11223344;
const int num = 4096;

key_t getkey()
{
    key_t key = ftok(path.c_str(), proj_id);
    if(key < 0)
    {
        std::cerr << "getkey error" << std::endl;
        exit(-1);
    }
    return key;
}

int _getshm(key_t key, int flag)
{
    int shmid = shmget(key,num,flag);
    if(shmid < 0)
    {
        std::cerr << "shm error" << std::endl;
        exit(-1);
    }
    return shmid;
}   

int Create_shm(key_t key)
{
    return _getshm(key,IPC_CREAT|IPC_EXCL|0644);
}

int getshm(key_t key)
{
    return _getshm(key,IPC_CREAT);
}

bool Create_fifo()
{
    int n = mkfifo(filename.c_str(),0666);
    if(n < 0)
    {
        std::cerr << "mkfifo error" << std::endl;
        return false;
    }

    return true;
}

Server.cc

#include "Com.hpp"

class Init
{
public:
    Init()
    {
        //创建命名管道
        Create_fifo();
        // 获取key
        key_t key = getkey();
        // 创建共享内存
        shmid = Create_shm(key);
        std::cout << shmid << std::endl;
        std::cout << "chuangjian" << std::endl;
        // 挂载
        addr = (char *)shmat(shmid, nullptr, 0);
        std::cout << "guazai" << errno << std::endl;
        fd = open(filename.c_str(),O_RDONLY);


    }

    ~Init()
    {
        shmdt(addr);
        std::cout << "qu" << std::endl;
        // 删除共享内存
        shmctl(shmid, IPC_RMID, nullptr);
        std::cout << "shan" << std::endl;
        close(fd);
        unlink(filename.c_str());
    }

public:
    int shmid;
    char *addr;
    int fd;
};

Init init;
int main()
{

    while(true)
    {
        int code = 0;
        ssize_t n = read(init.fd,&code,sizeof code);
        if(n > 0)
        {
            std::cout << init.addr << std::endl;
        }
        else if(n == 0)
        {
            break;
        }
        
        //sleep(1);
    }

    return 0;
}

Client.cc

#include "Com.hpp"


class Init
{
public:
    Init()
    {
        // 获取key
        key_t key = getkey();
        // 创建共享内存
        shmid = getshm(key);
        // 挂载
        addr = (char *)shmat(shmid, nullptr, 0);
    }

    ~Init()
    {
        shmdt(addr);
    }

public:
    int shmid;
    char *addr;
};

Init init;

int main()
{
    int fd = open(filename.c_str(),O_WRONLY);
    for(char ch = 'a'; ch <= 'z'; ++ch)
    {
        int code = 1;
        write(fd,&code,sizeof code);
        init.addr[ch-'a'] = ch;

        sleep(1);
    }
    return 0;
}

我们是可以通过命名管道让共享内存拥有同步机制的。

总结

  1. 共享内存的通信方式,不会提供同步机制,共享内存是直接裸露给所有的使用者的,所以一定要注意共享内存的使用安全问题。
  2. 共享内存可以提供较大的空间。
  3. 共享内存是所有进程间通信中速度最快的。可以减少数据拷贝的次数。

为什么说共享内存是最快的?
这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据,也就会减少很多的数据的拷贝。

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

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

相关文章

数仓开发,分层(ods,dw,app层)

1、从数据源中导入源数据&#xff0c;到ODS表&#xff0c;作为事实表的数据 2、可以根据自己的开发设计&#xff0c;是否单独分支出来一个维度表&#xff0c;帮助和协助处理源数据表ODS层 和需求层ADS&#xff08;APP&#xff09;层 3、现在我们有了一个事实ODS层&#xff0…

【R语言】边缘概率密度图

边缘概率密度图是一种在多变量数据分析中常用的图形工具&#xff0c;用于显示每个单独变量的概率密度估计。它通常用于散点图的边缘&#xff0c;以便更好地理解单个变量的分布情况&#xff0c;同时保留了散点图的相关性信息。 在边缘概率密度图中&#xff0c;每个变量的概率密度…

Linux-信号保存

1. 概念 进程执行信号的处理动作&#xff0c;称为 信号递达&#xff08;Delivery&#xff09; 信号从产生到递达之间的状态&#xff0c;称为 信号未决&#xff08;Pending&#xff09; 进程可以选择 阻塞&#xff08;Block&#xff09;某个信号 过程&#xff1a; 信号产生 ——…

Java的BIO/NIO/AIO

1. Java中的BIO、NIO和AIO的基本概念及其主要区别 BIO (Blocking I/O): 传统的同步阻塞I/O模型。每个连接创建成功后都需要一个线程来处理&#xff0c;如果连接没有数据可读&#xff0c;则线程会阻塞在读操作上。这种模型简单易理解&#xff0c;但在高并发环境下会消耗大量系统…

苹果Mac用户下载VS Code(Universal、Intel Chip、Apple Silicon)哪个版本?

苹果macOS用户既可以下载通用版&#xff08;Universal&#xff09;&#xff0c;软件将自动检测用户的处理器并进行适配。 也可以根据型号下载对应CPU的版本&#xff1a; 使用Intel CPU的Mac电脑可下载Intel Chip版本&#xff1b; 使用苹果自研M系列CPU的Mac电脑下载Apple Si…

Animation: (1) animatedline

目录 示例1&#xff1a;显示线条动画示例2&#xff1a;指定动画线条颜色示例3&#xff1a;指定日期时间和持续时间值示例4&#xff1a;设置最大点数示例5&#xff1a;批量添加点以生成快速动画示例6&#xff1a;使用drawnow limitrate创建快速动画示例7&#xff1a;定时更新屏幕…

如何获取中国各省市区的边界

前几个专栏我介绍了获取各流域边界的方法&#xff0c;可参见以下的文章&#xff1a; 格林兰岛和南极洲的流域边界文件下载-CSDN博客 读取shp文件中的经纬度坐标-CSDN博客 读取谷歌地球的kml文件中的经纬度坐标_谷歌地球识别穿过矿区的公路,并获取公路的经纬度坐标-CSDN博客 关于…

docker-compose部署gitlab

需要提前安装docker和docker-compose环境 参考&#xff1a;部署docker-ce_安装部署docker-ce-CSDN博客 参考&#xff1a;docker-compose部署_docker compose部署本地tar-CSDN博客 创建gitlab的数据存放目录 mkdir /opt/gitlab && cd mkdir /opt/gitlab mkdir {conf…

算法学习Day2——单调栈习题

第一题&#xff0c;合并球 题解&#xff1a;一开始写了一次暴力双循环&#xff0c;直接O(n^2)严重超时&#xff0c;后面于是又想到了O(n)时间复杂度的链表&#xff0c;但是还是卡在 最后一个数据会TLE&#xff0c;我也是高兴的拍起来安塞腰鼓和华氏护肤水&#xff0c;后面学长给…

内网安全【2】——域防火墙/入站出站规则/不出网隧道上线/组策略对象同步

-隧道技术&#xff1a;解决不出网协议上线的问题(利用出网协议进行封装出网)&#xff08;网络里面有网络防护&#xff0c;防火墙设置让你不能正常访问网络 但有些又能正常访问&#xff0c;利用不同的协议tcp udp 以及连接的方向&#xff1a;正向、反向&#xff09; -代理技术&…

《ESP8266通信指南》13-Lua 简单入门(打印数据)

往期 《ESP8266通信指南》12-Lua 固件烧录-CSDN博客 《ESP8266通信指南》11-Lua开发环境配置-CSDN博客 《ESP8266通信指南》10-MQTT通信&#xff08;Arduino开发&#xff09;-CSDN博客 《ESP8266通信指南》9-TCP通信&#xff08;Arudino开发&#xff09;-CSDN博客 《ESP82…

数据库管理-第185期 23ai:一套关系型数据干掉多套JSON存储(20240508)

数据库管理185期 2024-05-08 数据库管理-第185期 23ai:一套关系型数据干掉多套JSON存储&#xff08;20240508&#xff09;1 上期示例说明2 两个参数2.1 NEST/UNNEST2.2 CHECK/NOCHECK 3 一数多用3.1 以用户维度输出订单信息3.2 以产品维度3.3 以产品种类维度 4 美化输出总结 数…

出差——蓝桥杯十三届2022国赛大学B组真题

问题分析 该题属于枚举类型&#xff0c;遍历所有情况选出符合条件的即可。因为只需要派两个人&#xff0c;因此采用两层循环遍历每一种情况。 AC_Code #include <bits/stdc.h> using namespace std; string str;//选择的两人 bool ok(){if(str.find("A")!-1…

SwiGLU激活函数

SwiGLU激活函数已经成为LLM的标配了。它是GLU的变体&#xff0c;公式如下&#xff1a; SwiGLU ⁡ ( x , W , V , b , c , β ) Swish ⁡ β ( x W b ) ⊗ ( x V c ) \operatorname{SwiGLU}(x, W, V, b, c, \beta)\operatorname{Swish}_\beta(x Wb) \otimes(x Vc) SwiGLU(x,…

CSS---复合选择器和元素显示模式(三)

一、CSS的复合选择器 1.1 什么是复合选择器 在CSS中&#xff0c;可以根据选择器的类型把选择器分为基础选择器和复合选择器&#xff0c;复合选择器是建立在基础选择器之上&#xff0c;对基本选择器进行组合形成的。 复合选择器是由两个或多个基础选择器连写组成&#xff0c;它…

从Python整数变量内存大小占用28字节谈起

实验结果 本机环境64位Python 3.12 内存布局图 0 4 8 12 16 20 24 28 |----------|----------|----------|----------|----------|----------|----------| | ob_refcnt | ob_type | ob_digit | …

【大数据】分布式数据库HBase下载安装教程

目录 1.下载安装 2.配置 2.1.启动hadoop 2.2.单机模式 2.3.伪分布式集群 1.下载安装 HBase和Hadoop之间有版本对应关系&#xff0c;之前用的hadoop是3.1.3&#xff0c;选择的HBase的版本是2.2.X。 下载地址&#xff1a; Index of /dist/hbase 配置环境变量&#xff1a…

红米1s 刷入魔趣 (Mokee)ROM(Android 7.1)

目录 背景准备工具硬件&#xff08;自己准备&#xff09;软件&#xff08;我会在文末提供链接&#xff09; 刷机步骤1. 重启电脑2. 安装驱动3. 刷入TWRP4. 清空数据5. 刷入魔趣6. 开机 结尾下载链接 本文由Jzwalliser原创&#xff0c;发布在CSDN平台上&#xff0c;遵循CC 4.0 B…

LeetCode 138. 随机链表的复制

目录 1.原题链接&#xff1a; 2.结点拆分&#xff1a; 代码实现&#xff1a; 3.提交结果&#xff1a; 4.读书分享&#xff1a; 1.原题链接&#xff1a; 138. 随机链表的复制 2.结点拆分&#xff1a; ①.拷贝各个结点&#xff0c;连接在原结点后面&#xff1b; ②.处…

Lora基础炼丹学习笔记

1、收集数据集 20-30张人物各个角度、各个姿势的图片 2、图片预处理 裁剪 打标签 裁剪必须也要512 * 512 &#xff0c;因为sd1.5就是用这个尺寸训练的&#xff0c;可以使用后期处理 打标可以勾选这个&#xff0c;Deepbooru对二次元画风更友好 打标也可以使用wb14-tagger的…