【Linux】进程间通信-共享内存

news2024/9/25 3:21:23

前言

        我们知道,在Linux中,进程是相互独立存在的,不存在直接让进程之间互相通信的方式。但是如果我们能让不同进程之间见到同一块内存,也就是都能读写这片区域是不是就能够达到进程间通信呢?

        事实证明确实如此。在之前我们可以利用管道来实现出进程之间的通信。(匿名管道和命名管道)但是似乎在传输过程中都是通过操作系统之手(系统调用)来完成的。那么进程之间能否拥有一块属于自己管辖的共享的内存呢?这篇文章就是关于System V共享内存方式实现进程间通信的学习笔记~

 『但是呢,我遇到了美丽的天使』

上一篇Linux学习笔记传送门!

【Linux】静动态库的制作和使用_柒海啦的博客-CSDN博客

目录

一、理解一个共享内存

二、共享内存相关接口

1.ftok创建唯一key

2.shmget获取共享内存

3.shmctl操作共享内存

4.shmat挂接共享内存

5.shmdt与共享内存去关联

三、简单demo 


一、理解一个共享内存

        顾名思义,共享内存就是进程之间可以共享的内存。

        此内存就是操作系统开辟的一段内存,通过页表映射-到进程地址空间里的共享区,就可以让不同进程之间共享内存。释放的时候先去掉映射,然后释放掉即可。

         其实,当不同进程之间挂接了共享内存后,此时ipc(Inter-Process Communication-进程间通信)是最快的,因为数据传输之间不在涉及内核,即不通过系统调用来传递彼此的信息。

        共享内存同样是操作系统申请的,那么操作系统也需要对其进行管理(先描述在组织),所以对于共享内存的组成实际上就是:共享内存 = 共享内存块 + 共享内存的内核数据结构。内核数据结构存放相关信息,比如挂接的进程个数等等。

        那么我们如何让操作系统创建共享内存,并且让不同进程挂接到同一个共享内存呢?

二、共享内存相关接口

1.ftok创建唯一key

        类似于标识符一样。为了区分不同的共享内存段的名称,我们就需要一个独一无二的共享内存段名字。

(man 3 ftok)

函数原型:

        key_t ftok(const char *pathname, int proj_id);

依赖头文件:

        #include <sys/types.h>
        #include <sys/ipc.h>

参数:

        pathname:给定一个现有的路径-必须有权限。(方便创建文件)

        proj_id:大小不超过8bit的数字,即一个字节的内的数据(0 ~ 255)

返回值:

        key_t:typedef int 类似于标识符key,用于下面获取共享内存的参数。如果返回值小于零表示创建失败。

注意:

        不一定百分百创建成功,可以多试几次。

2.shmget获取共享内存

        有了一个唯一的标识符后,我们可以利用此标识符通过系统调用获取一段可以供进程挂接的共享内存。获取可以是找现有的或者重新创建。一般在实际运用中服务端创建,客户端进行获取。

(man 2 shmget)

函数原型:

        int shmget(key_t key, size_t size, int shmflg);

依赖头文件:

        #include <sys/ipc.h>
        #include <sys/shm.h>

参数:

        key:利用ftok算法函数生成的唯一标识符。

        size:自定义共享内存的开辟大小,以字节为单位。(最好是页的大小(4KB)的整数倍)

        shmflg:操作数。(一共由九个权限标志构成,用法和创建文件时使用的mode模式标志是一样的)

                IPC_CREAT        单独如果创建共享,底层存在,会获取并且返回,不存在创建就返回。(Create key if key does not exist.)

                IPC_EXCL        单独使用没有价值,和IPC_CREAT一起使用如果底层不存在创建并且返回。底层存在出错并且返回。(Fail if key exists.)

                权限码(对文件的权限【Linux】权限管理_柒海啦的博客-CSDN博客)

                0:取共享内存标识符,若不存在则函数会报错。

                ......

(至少包含 IPC_CREAT | 权限码

返回值

        成功就返回合法的标识符,用于表示此段共享内存的编号(类似于文件操作里的fd,这里就是shm+id),失败的话返回-1并且设置错误码。

注意:

        开辟的内存空间大小之所以是页的整数倍是为了防止浪费。虽然在监视窗口(ipcs -m 可以查看共享内存资源ipcrm -m shmid可以删除对应的共享内存-命令操作)是你设定的那样,但是实际上操作系统是四舍五入的提供的内存,即比如你要了1个字节的共享内存,实际上操作系统分配的是一整页的内存,然后其余内存就没法用内存不就造成了资源的浪费嘛。

3.shmctl操作共享内存

        通常,我们可以利用这个系统调用来释放掉我们所创建的共享内存。

(man 2 shmctl)

函数原型:

        int shmctl(int shmid, int cmd, struct shmid_ds *buf);

依赖头文件:

        #include <sys/ipc.h>
        #include <sys/shm.h>

参数:

        shmid:是shmget接口获取共享内存返回的id,表示操作此段共享内存。

        cmd:操作数。

                IPC_RMID        释放此共享内存(无论是否存在进程和其挂接)

                ......

        buf:是指向shmid_ds strucc‐的指针,一般情况下我们无需关系,设置为nullptr即可。

返回值

        成功操作返回0,失败返回-1。

注意:

        通常情况下在服务端最后结束的时候使用此接口对创建了的共享内存进行删除,所以真正用到的参数也就两个,操作数也就只有一个。

4.shmat挂接共享内存

        在获取到共享内存的id后,就要和本身进程地址空间进行一个挂接,获取到对应的地址,准备进程间通信。

(man 2 shmat)

函数原型:

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

依赖头文件:

        #include <sys/types.h>
        #include <sys/shm.h>

参数:

        shmid:是shmget接口获取共享内存返回的id,表示操作此段共享内存。

        shmaddr:一般是挂接的形式相关,一般不进行操作,设置为nullptr即可。

        shmflg:操作数。

                0:取共享内存标识符,若不存在则函数会报错。

                .......

返回值

        成功挂接上返回此共享内存的地址。挂接失败返回(void*)-1,并且设置错误码。

注意:

        此接口的目的是和当前进程进行挂接,初学阶段不必了解挂接的形式相关,只需要挂接到即可,并且知道挂接成功返回的是共享内存的地址,就和平时我们操作的地址类似。

5.shmdt与共享内存去关联

        在客户端(服务端可以不用去关联-最后会进行释放)最后我们需要将此进程和共享内存之间的连接断开就可以使用此系统接口。

(man 2 shmdt)

函数原型:

        int shmdt(const void *shmaddr);

依赖头文件:
        #include <sys/types.h>
        #include <sys/shm.h>

参数:

        shmaddr:共享内存地址。(可以是上面挂接shmat函数的返回的共享内存地址值)

返回值:

        去关联成功返回0,失败返回-1,设置错误码。

注意:

        共享内存和进程之间去关联函数。

三、简单demo 

        下面这段简单代码就演示简单的服务端创建共享内存,服务端、客户端挂接共享内存,服务端读取共享内存,客服端写入共享内存达成进程间通信。最后服务端和客户端去关联共享内存,服务端释放共享内存,结束。(关闭信号可以用quit进行识别)

        利用上面介绍的接口就可以完成上述要求,快来试试吧~

//common.h
#pragma once

#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <cstdio>
#include <string>
#include <cstring>

#define PATH_NAME "/home/QiHai/code/2022/TestProject"
#define PROJ_ID 0x69  // 0 ~ 255 非零
#define SHMSIZE 4096  // 页的整数倍(4KB = 4 * 1024byte) - 否则就会造成空间上的浪费
//client.cpp
#include "common.h"
// 服务端
int main()
{
    // 首先生成秘钥
    key_t key = ftok(PATH_NAME, PROJ_ID);
    // std::cout << key << std::endl;

    // 创建共享内存
    umask(0);  // 掩码
    int shmid = shmget(key, SHMSIZE, IPC_CREAT | IPC_EXCL | 0666);
    if (shmid < 0)
    {
        perror("shmget:");
        exit(1);
    }
    std::cout << "创建共享内存成功!" << std::endl;

    // 挂接到当前进程的共享区
    char* shmarr = (char*)shmat(shmid, nullptr, 0);  // 共享内存id 挂接形式... 操作
    if (*((int*)shmarr) < 0)
    {
        perror("shmat:");
        exit(2);
    }
    std::cout << getpid() << "进程挂接共享内存成功!" << std::endl;

    // 进程通信 服务端进行循环读取,由于是直接访问内存,所以没有阻塞,需要配合管道食用更佳
    while (true)
    {
        char* tmp = shmarr;
        if (strcmp(shmarr, "quit") == 0) break;
        printf("%s\n", tmp);
        sleep(1);
    }

    // 去关联 - 实际上服务端可以不用做这一步,因为服务端最后会将此共享内存给删除
    int s = shmdt(shmarr);
    if (s < 0)
    {
        perror("shmdt:");
        exit(2);
    }
    std::cout << getpid() << "进程去关联成功!" << std::endl;
    // 删除共享内存
    int m = shmctl(shmid, IPC_RMID, nullptr);  // 共享内存id 操作(当前操作是无论有多少个进程挂接上都会删除掉) null
    if (m < 0)
    {
        perror("shmctl:");
        exit(3);
    }
    std::cout << "删除共享内存成功!" << std::endl;

    return 0;
}
//server.cpp
#include "common.h"
// 客户端
int main()
{
    // 首先生成秘钥
    key_t key = ftok(PATH_NAME, PROJ_ID);

    int shmid = shmget(key, SHMSIZE, 0);  // 客户端给0即可
    if (shmid < 0)
    {
        perror("shmget:");
        exit(1);
    }

    // 挂接到当前进程的共享区
    char* shmarr = (char*)shmat(shmid, nullptr, 0);  // 共享内存id 挂接形式... 操作
    if (*((int*)shmarr) < 0)
    {
        perror("shmat:");
        exit(2);
    }
    std::cout << getpid() << "进程挂接共享内存成功!" << std::endl;

    // 进程通信 客户端写即可
    std::string buffer;
    while (true)
    {
        std::getline(std::cin, buffer);  // 等待键盘输入
        snprintf(shmarr, buffer.size() + 1, "%s", buffer.c_str());
        if (buffer == "quit") break;
    }

    // 去关联
    int s = shmdt(shmarr);
    if (s < 0)
    {
        perror("shmdt:");
        exit(2);
    }
    std::cout << getpid() << "进程去关联成功!" << std::endl;
    return 0;
}

运行结果:

         服务端会不断的读取客户端的输入数据。当然可以控制如果客户端不输入就不进行读入(利用管道的特性),那么中间就要加上管道操作就可以实现了。

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

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

相关文章

【springboot进阶】基于starter项目构建(二)构建starter项目-fastjson

目录 一、创建 fastjson-spring-boot-starter 项目 二、添加 pom 文件依赖 三、构建配置 四、加载自动化配置 五、打包 六、使用 这个系列讲解项目的构建方式&#xff0c;主要使用 父项目 parent 和 自定义 starter 结合。项目使用最新的 springboot3 和 jdk19。本系列的…

kinect v2安装iai_kinect2

目前已完成 前期已经安装了libfreenect2 使用的系统为ubuntu 18.04 使用的相机为kinect v2 已经安装好了orb_slam3&#xff0c;已经完成使用stereo在euroc数据集和使用RGB-D在tum数据集上的测试 目的 想要完成使用深度相机进行在线测试 步骤 step1 经过查阅资料发现目前仅…

大学生HTML期末作业, JavaScript期末大作业

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

Java项目:SSM设备台账管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 管理员角色包含以下功能&#xff1a; 管理员登录,物理设备管理,IP地址资源管理,虚拟机管理,通知公告管理,学历管理,部门管理,员工管理等功能。 …

DFS 数据结构 C++语言实现 图的深度优先遍历

1.DFS图解 图的深度优先遍历 1.1 基本定义&#xff1a; 设初始时&#xff0c;图中所有顶点未曾被访问过&#xff1a; ● 从图中某个顶点 v 出发&#xff0c;访问此顶点&#xff1b; ● 依次从 v 的未被访问的邻接点出发深度优先遍历图&#xff0c;直至图中所有和顶点 v 有路…

【非下载vs解决】error: Microsoft Visual C++ 14.0 or greater is required

首先说解决办法 搜索下载对应库的whl文件即可 下面是解决过程 部分报错为&#xff1a;error: Microsoft Visual C 14.0 or greater is required. Get it with “Microsoft C Build Tools”: https://visualstudio.microsoft.com/visual-cpp-build-tools/ 我是安装wordcloud库…

Activemq安装和控制台

目录 一、安装 二、后台服务启动 三、查看前台 一、安装 http://blog.csdn.net/gebitan505/article/details/55096222 二、后台服务启动 普通启动/关闭 到activemq的目录下执行【./activemq start/stop】进行开启和关闭activemq 带日志的启动 控制台不输出东西&#x…

Stm32旧版库函数2——mpu6050 移植成旧版兼容型库函数DMP

main.c: /******************************************************************************* // 陀螺仪 MPU6050 IIC测试程序 // 使用单片机STM32F103C8T6 // 晶振&#xff1a;8.00M // 编译环境 Keil uVision4 // 在3.3V的供电环境下&#xff0c;就能运行 // 波特率 9600 /…

STM32通过DAC产生正弦波

前言 这一讲主要来讲解DAC功能 文章目录 前言一、DAC简介二、DAC通道框图三、DAC输出电压四、输出正弦波五、代码一、DAC简介 数字/模拟转换模块(DAC)是12位数字输入,电压输出的数字/模拟转换器。DAC可以配置为8位或12位模式,也可以与DMA控制器配合使用。DAC工作在12位(1…

[机缘参悟-93]:时间、空间、多维度、动态、全局、系统思考模型汇总

目录 前言&#xff1a; 一、空间 - 广度 - 静态 - 多维度模型 1.1 一元太极本源模型 1.2 二元阴阳组合模型 1.3 三元铁三角稳定模型 1.4 四象限优先级模型 1.5 九宫格二维矩阵模型 二、空间 - 高度 - 静态 - 多层次模型 2.1 倒树形层次模型 2.2 金字塔层次结构模型 …

新年新气象:Stimulsoft Designer 2023.1.0 Crack

使用 Stimulsoft Designer&#xff0c;您可以轻松设计从简单列表到多页、具有复杂计算、条件、函数和变量的报告。只需单击几下&#xff0c;您就可以创建易于理解且信息丰富的仪表板、设置过滤器以及对任何元素进行排序。Ω578867473 报表设计器很简单 这是一个不需要编程技能的…

6 转移指令

转移指令 1 数据存储位置的表示 我们定义的描述性符号&#xff1a; reg 和sreg 。使用描述性的符号reg 来表示一个寄存器&#xff0c;用sreg 表示一个段寄存器。 reg 的集合包括&#xff1a; ax 、bx 、ex 、dx 、ah 、al 、bh 、bl 、ch 、cl 、dh 、di 、sp 、bp 、si 、d…

NAT (Network Address Translations) 网络地址转换

数据来源 1、ipv4地址严重不够用了 X.X.X.X X 0-255 A、B、C类可以使用 D组播 E科研 2、IP地址分为公网IP和私网IP 公网IP只能在公网上使用私网IP只能在内网中使用公网上不允许出现私有IP地址私网IP可以重复在内网使用1&#xff09;私有地址范围 10.0.0.0/8&…

Vite+Vue3构建前端框架及模板代码及重要知识点

Vue3Vite构建步骤 用vite初始化vue项目(回车) npm create vitelatest vueVitePro -- --template vue安装配置路由vue-router npm install vue-router4 import router from ./router/index.js createApp(App).use(router).mount(#app) 安装 element-plus 及图标 npm ins…

一个PCA加速技巧

EVD-PCA PCA推导&#xff1a;PCA主成分分析法浅理解 具体数值如1030410304是我机器学习课程实验的数据集参数&#xff0c;这里关注数字量级即可。 code % EVD-PCA数据降维 % input: DN output:KN function [Z, K] EVD_PCA(X, K, weight)fprintf(Running EVD-PCA dimension…

Matplotlib学习笔记(第二章 2.13 Matplotlib中的图形(二))

路径(Paths) 你可以使用matplotlib.path模块在Matplotlib中添加任意路径&#xff1a; Fig. 6: Path Patch 三维绘图(Three-dimensional plotting) mplot3d工具包(参见see Getting started and mplot3d-examples-index))支持简单的3D图形&#xff0c;包括曲面、线框、散点图和…

【华为上机真题 2022】玩牌高手

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

【OpenCV-Python】教程:4-5 SURF (Speeded-Up Robust Features) 介绍

OpenCV Python SURF &#xff08;Speeded-Up Robust Features&#xff09; 介绍 【目标】 SURF的基础 【理论】 SURF 是 SIFT 的提速版本&#xff1b; 在SIFT中&#xff0c;Lowe用 DoG 近似 LoG&#xff1b;SURF 走的更远一点&#xff0c;用 box filter 近似 LoG 。下图显…

文本生成公开数据集/开源工具/经典论文详细列表分享

这是一份由清华大学自然语言处理小组整理的文本生成相关的公开数据集/开源工具/经典论文列表&#xff0c;并且不断增加论文和持续修改名单&#xff0c;分享给大家。 源链接&#xff1a;https://github.com/THUNLP-MT/TG-Reading-List 目录 数据集 故事生成 文本生成 工具 经典…

开关电源环路稳定性分析(06)-功率级和控制级

大家好&#xff0c;这里是大话硬件。 根据上一篇文章的分析&#xff0c;开关电源系统主要分为3个部分&#xff0c;功率级&#xff0c;控制级&#xff0c;反馈级。今天这篇文章我们分析功率级和控制级的传递函数。 1.功率级传递函数 从功能框图上可以看出来&#xff0c;功率…