进程间通信(IPC)的方法:共享内存

news2025/1/14 1:02:42

      共享内存(shared memory)是可用IPC技术中最快的一种。一旦内存被映射到共享内存区域的进程的地址空间中,在进程之间传递数据时就不会发生内核(kernel)参与。然而,在共享内存区域存储和提取数据时,进程之间需要某些形式的同步(例如互斥锁、条件变量、读写锁、记录锁、信号量)。
      注:不允许共享对象文件名中任何位置出现"/"(不包括前导"/")
      关键步骤:
      (1).使用shm_open函数创建共享内存对象;
      (2).使用mmap函数将共享内存区域映射到RAM,使其在内存中拥有可读写的地址.

      共享内存总结:
      (1).异步;
      (2).物理内存数据大小限制;
      (3).多个进程可以同时访问但需要同步管理;
      (4).随机访问。

      注:以上内容主要来自网络整理。

      测试代码如下:

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <iostream>
#include <thread>

const char* SHARED_OBJ_NAME = "shm_test"; // disallows '/' from anywhere in the filename(not counting leading '/'): shm_test will be generated in /dev/shm/ directory

// shared data struct
typedef struct message {
    int pid;
    int counter;
} message;

bool write_message(int pid, int value)
{
    auto fd = shm_open(SHARED_OBJ_NAME, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); // create and open a new shared memory object, return a file descriptor
	if (fd == -1) {
		fprintf(stderr, "fail to shm_open: %s\n", strerror(errno));
		exit(1);
	}

	auto ret = ftruncate(fd, sizeof(message)); // truncate a file to a specified length
	if (ret == -1) {
		fprintf(stderr, "fail to ftruncate: %s\n", strerror(errno));
		exit(1);
	}

    message* msg_ptr = (message*)mmap(nullptr, sizeof(message), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // create a new mapping in the virtual address space of the calling process
	if (msg_ptr == MAP_FAILED) {
		fprintf(stderr, "fail to mmap: %s\n", strerror(errno));
		exit(1);
	}
    
    fprintf(stdout, "Process %d: Increase the counter\n", pid);
    msg_ptr->pid = pid;
    msg_ptr->counter = value;

    ret = munmap(msg_ptr, sizeof(message)); // delete the mappings for the specified address range
	if (ret == -1) {
		fprintf(stderr, "fail to munmap: %s\n", strerror(errno));
		exit(1);
	}

    // remember to close to not hit an error of opening too many files
    close(fd);
    return true;
}

bool read_message(int curr_pid, int& curr_value)
{
    int fd = shm_open(SHARED_OBJ_NAME, O_RDWR, S_IRUSR | S_IWUSR);
	if (fd == -1) {
		fprintf(stderr, "fail to shm_open: %s\n", strerror(errno));
		exit(1);
	}

    auto ret = ftruncate(fd, sizeof(message));
	if (ret == -1) {
		fprintf(stderr, "fail to ftruncate: %s\n", strerror(errno));
		exit(1);
	}

    message* msg_ptr = (message*)mmap(nullptr, sizeof(message), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (msg_ptr == MAP_FAILED) {
		fprintf(stderr, "fail to mmap: %s\n", strerror(errno));
		exit(1);
	}

    if (msg_ptr->pid == curr_pid) {
        fprintf(stderr, "Process %d: No new msg available\n", curr_pid);
		munmap(msg_ptr, sizeof(message));
		close(fd);
		shm_unlink(SHARED_OBJ_NAME);
        return false;
    } else {
        fprintf(stdout, "Process %d: Receive %d from PID %d\n", curr_pid, msg_ptr->counter, msg_ptr->pid);
        curr_value = msg_ptr->counter;
        munmap(msg_ptr, sizeof(message));
    }

    close(fd);
	shm_unlink(SHARED_OBJ_NAME); // remove an object previously created by shm_open
    return true;
}

int main()
{
	// reference: https://biendltb.github.io/tech/inter-process-communication-ipc-in-cpp/
    write_message(-1, 0); // Init the initial value

    // create a child process by calling folk, 
    pid_t pid = fork();
    if (pid < 0) {
        fprintf(stderr, "fail to fork\n");
        return -1;
    }

    if (pid != 0) { // parent process
        for (int i = 0; i < 5; i++) {
            int value;
            // only write message if reading sucessfully
            if (read_message(pid, value))
                write_message(pid, ++value);
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }

		int status;
        auto pid2 = wait(&status); // system call suspends execution of the calling thread until one of its children terminates
        fprintf(stdout, "process ID of the terminated child: %d\n", pid2);
        if (WIFEXITED(status)) { // returns true if the child terminated normally
            fprintf(stdout, "child process ended with: exit(%d)\n", WEXITSTATUS(status));
        }
        if (WIFSIGNALED(status)) { // returns true if the child process was terminated by a signal
            fprintf(stderr, "child process ended with: kill -%d\n", WTERMSIG(status));
        }
    }

    if (pid == 0) { // child process
        for (int j = 0; j < 5; j++) {
            int value;
            if (read_message(pid, value))
                write_message(pid, ++value);
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }

		exit(0);
    }

    fprintf(stdout, "====== test finish ======\n");
    return 0;
}

      编译脚本build.sh内容如下:

#! /bin/bash

if [ -d build ]; then
    echo "build directory already exists, it does not need to be created again"
else
    mkdir -p build
fi

cd build
cmake ..
make

rc=$?
if [[ ${rc} != 0 ]];then
    echo "#### ERROR: please check ####"
    exit ${rc}
fi

echo "==== build finish ===="

      CMakeLists.txt内容如下:

cmake_minimum_required(VERSION 3.22)
project(samples_multi_process)

set(CMAKE_BUILD_TYPE Release) # only works under linux
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -O2 -std=c++17")

file(GLOB samples ${PROJECT_SOURCE_DIR}/test_*.cpp)
#message(STATUS "samples: ${samples}")

foreach(sample ${samples})
    string(REGEX MATCH "[^/]+$" name ${sample})
    string(REPLACE ".cpp" "" exec_name ${name})
    #message(STATUS "exec name: ${exec_name}")

    add_executable(${exec_name} ${sample})
    target_link_libraries(${exec_name} rt)
endforeach()

      执行结果如下所示:

      GitHub:https://github.com/fengbingchun/Linux_Code_Test

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

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

相关文章

Spring Boot的魔法:构建高效Java应用的秘诀

文章目录 1. 自动配置&#xff08;Auto-Configuration&#xff09;2. 起步依赖&#xff08;Starter Dependencies&#xff09;3. 内嵌Web服务器&#xff08;Embedded Web Server&#xff09;4. 外部化配置&#xff08;Externalized Configuration&#xff09;5. Spring Boot Ac…

每日一题 213. 打家劫舍 II

难度&#xff1a;中等 思路&#xff1a; 首先不看成环&#xff0c;只是当作列表&#xff0c;那么对于第 i 间房&#xff0c;到 i 为止的最高偷窃金额为 f(i) max(f(i - 1), f(i - 2) nums[i])分析递推关系第一点&#xff0c;不管 i - 2 处的房子是否偷窃&#xff0c;i 处的…

【基础篇】ClickHouse 表结构设计

文章目录 1. ClickHouse 表结构设计1. 表的创建与标准SQL的差异1. 创建普通表2. 创建物化视图3. 创建分布式表 2. 表引擎1. MergeTree:2. Log:3. Memory:4. Distributed:5. Kafka:6. MaterializedView:7. File和URL: 3. MergeTree 家族3.1. MergeTree:3.2. ReplacingMergeTree:…

Nacos单机启动的两种方式

说明&#xff1a;直接双击nacos的启动脚本&#xff0c;默认是集群&#xff08;cluster&#xff09;的方式&#xff1b; 需要单机启动&#xff0c;有以下两种方式&#xff1b; 方式一&#xff1a;命令行 在当前目录打开命令窗口&#xff0c;输入以下命令启动nacos startup.…

jdk1.8堆内存学习

jdk1.8堆内存启动时控制参数图解 堆大小年轻代&#xff08;Young Generation&#xff09;年老代&#xff08;Old Generation&#xff09; GC相关 -Xnoclassgc&#xff1a;关闭JVM垃圾回收功能 -XX:UseSerialGC&#xff1a;使用Serial垃圾收集器&#xff0c;单线程串型收集器&…

Linux安装包 | Git使用 | NFC搭建

dpgt使用 当谈到基于 Debian 的操作系统中的软件包管理工具时&#xff0c;dpkg 是一个重要的工具。它是 Debian 系统中用于安装、升级、配置和卸载软件包的命令行工具。以下是对 dpkg 的详细介绍&#xff1a; 软件包管理&#xff1a;dpkg 可以管理系统中的软件包。它可以安装单…

Acwing 829. 模拟队列

Acwing 829. 模拟队列 题目描述思路讲解代码展示 题目描述 思路讲解 队列是先进先出&#xff0c;类比排队买饭 代码展示 #include <iostream>using namespace std;const int N 100010;int m; int q[N], hh, tt -1;int main() {cin >> m;while (m -- ){string …

ArmSoM-W3之RK3588 Debian11详解

1. 简介 RK3588从入门到精通Debian 是⼀种完全⾃由开放并⼴泛⽤于各种设备的 Linux 操作系统。Rockchip在官⽅Debian发⾏版的基础上构建和适配了相关硬件功能 2. 环境介绍 硬件环境&#xff1a; ArmSoM-W3 RK3588开发板 软件版本&#xff1a; OS&#xff1a;ArmSoM-W3 Debia…

服务器中了DevicData勒索病毒怎么办?勒索病毒解密,数据恢复

近日&#xff0c;云天数据恢复中心收到许多中了勒索病毒的用户求助。其中有多位用户中的都是同一种勒索病毒&#xff0c;它就是DevicData勒索病毒。那接下来我们就从它的特征、处理方案以及后续维护三个方面来了解一下这种勒索病毒。 一、DevicData勒索病毒的特征 加密文件&am…

VSCode 远程开发,再也不用带电脑回家了~

VS Code几乎是所有的程序员必备的工具之一&#xff0c;据说全球一般的开发者都使用过VS Code这款工具。 今天为大家介绍一下 VS Code 实现远程办公的方法。 1、概 述 通常&#xff0c;我们都是每天到工作的办公室进行办公&#xff0c;但是&#xff0c;如果下班回家&…

【多线程案例】定时器

1. 定时器是什么&#xff1f; 定时器也是软件开发中的一个重要组件. 类似于一个 "闹钟". 达到一个设定的时间之后, 就执行某个指定好的代码. 定时器是一种实际开发中非常常用的组件. 比如网络通信中, 如果对方 500ms 内没有返回数据, 则断开连接尝试重连. 比如一个 …

【Markdown】图片缩放

▚ 01 原图表示 语法为&#xff1a; ![替代文本](图片链接地址)其中&#xff0c;替代文本是在无法显示图片时显示的替代文本&#xff0c;而图片链接是指向图片的URL或相对路径。 例如&#xff0c;插入Panda图片&#xff1a; ![panda](https://img-blog.csdnimg.cn/e5f3…

李开复:我家的AI是坠吼的

创新工场董事长、鸿海独立董事李开复&#xff0c;近日出席鸿海股东会暨媒体记者会时表示&#xff0c;人工智能&#xff08;AI&#xff09;是人类史上即将面临的最伟大技术革命&#xff0c;未来十年的改变将改写人类历史、重构所有产业&#xff0c;其发展大致可分三阶段&#xf…

uniapp风险等级(三级)

代码 ​ <template><view><view class"riskGrade"><label>风险等级: </label><span v-if"flag 0 || flag 1 || 2" class"item":style"[{background:flag0?color:flag1?color:flag2?color:}]"…

快速排序和归并排序的非递归形式

快速排序和归并排序都需要用递归的形式展开&#xff0c;那么有没有什么方法不需要递归就能实现归并和快速排序&#xff0c;有的&#xff01; 1.快速排序 我们可以借助栈来模拟递归。 递归的主要思想就是大事化小&#xff0c;小事化了。我们借助栈的 目的是将需要排序的“头” 和…

面试题:有了 for 循环 为什么还要 forEach ?

文章目录 **本质区别****for循环和forEach的语法区别****for循环和forEach的性能区别** js中那么多循环&#xff0c;for for…in for…of forEach&#xff0c;有些循环感觉上是大同小异今天我们讨论下for循环和forEach的差异。我们从几个维度展开讨论&#xff1a; for循环和fo…

RJ45网络信号浪涌保护器解决方案

RJ45网络信号浪涌保护器是一种用于保护网络设备免受雷击或其他高压电流干扰的装置&#xff0c;它可以有效地吸收和释放信号线路上的过电压&#xff0c;从而避免设备损坏或数据丢失。 RJ45信号浪涌保护器的应用领域和施工方案如下&#xff1a; 地凯科技RJ45网络信号浪涌保护器…

合肥综合性国家科学中心人工智能研究院-机器学习作业(一)

1.试析min-max规范化和z-score规范化的优缺点 可参考博客&#xff1a;https://wenku.csdn.net/answer/fdbf30eb204644e5b69fc533a3757268 2.试分析损失函数与性能度量的关系 损失函数和性能度量之间的关系可以根据优化目标来理解。损失函数的优化目标是最小化预测值与实际值之…

laravel框架 - 开发实战(目录结构,路由,控制器,模型,视图)

一、laravel框架的目录结构 app:应用目录&#xff0c;保存项目中的控制器、模型等 bootstrap:保存框架启动的相关文件 config:配置文件目录 database:数据库迁移文件和数据填充文件 public:应用入口文件index.php和前端资源文件&#xff08;如CSS、JavaScript等&#xff09…

都2023年了你还不学ts (一)

TypeScript基础语法入门 TypeScript究竟是什么&#xff1f; 他主要就是想把JavaScript里面不完美的一些语法来进行一个提升 就像官网中所说的 TypeScript is JavaScript with syntax for types. 例如我们看下面的这段代码 if ("" 0) {console.log(hello) }在Jav…