linux应用 进程间通信之共享内存(POSIX)

news2025/1/19 10:33:45

1、前言

1.1 定义

POSIX共享内存是一种在UNIX和类UNIX系统上可用的进程间通信机制。它允许多个进程共享同一块内存区域,从而可以在这块共享内存上进行读写操作。

1.2 应用场景

POSIX共享内存适用于需要高效地进行大量数据交换的场景,比如多个进程需要共享大型数据集合或缓存。它可以提供比其他进程间通信方式更快的数据传输速度

1.3 优缺点

1.3.1 优点
  • 高效性:共享内存允许多个进程直接访问同一块内存,因此数据传输速度更快。
  • 灵活性:由于共享内存是直接映射到进程的地址空间,因此对数据的访问更加灵活。
1.3.2 缺点
  • 同步问题:需要额外的同步机制来确保多个进程对共享内存的访问是安全的。
  • 通信复杂性:共享内存通常需要与其他进程间通信方式(如信号量、互斥量等)结合使用。

2、常用接口

2.1 shm_open函数

创建或打开一个POSIX共享内存对象。

int shm_open(const char *name, int oflag, mode_t mode);

参数

  • name:共享内存对象的名称,以斜杠开头,类似于文件路径。
  • oflag:打开标志,可以使用 O_CREAT 表示创建共享内存对象,O_RDWR 表示读写模式等。
  • mode:创建共享内存对象时的权限模式。

返回值

  • 成功:返回一个非负整数,表示共享内存对象的文件描述符。
  • 失败:返回 -1,并设置 errno 以指示错误原因。

2.2 ftruncate函数

设置共享内存对象的大小。

int ftruncate(int fd, off_t length);

参数

  • fd:共享内存对象的文件描述符。
  • length:要设置的共享内存大小。

返回值

  • 成功:返回 0。
  • 失败:返回 -1,并设置 errno 以指示错误原因。

2.3 mmap函数

将共享内存对象映射到进程的地址空间。

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

参数

  • addr:指定映射的地址,通常为0表示由系统选择合适的地址。
  • length:映射的长度。
  • prot:映射的保护模式,如 PROT_READ 和 PROT_WRITE。
  • flags:映射的标志,如 MAP_SHARED 表示共享映射。
  • fd:共享内存对象的文件描述符。
  • offset:共享内存对象的偏移量。

返回值

  • 成功:返回映射区的起始地址。
  • 失败:返回 MAP_FAILED,并设置 errno 以指示错误原因。

2.4 munmap函数

解除共享内存的映射。

int munmap(void *addr, size_t length);

参数

  • addr:要解除映射的地址。
  • length:映射的长度。

返回值

  • 成功:返回 0。
  • 失败:返回 -1,并设置 errno 以指示错误原因。

2.5 shm_unlink函数

删除共享内存对象。

int shm_unlink(const char *name);

参数

  • name:共享内存对象的名称。

返回值

  • 成功:返回 0。
  • 失败:返回 -1,并设置 errno 以指示错误原因。

3、编程测试

3.1 共享内存简单测试

测试代码如下,代码编译需要加-lrt -pthread

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>

// 打印时分秒的宏        
#define PRINT_MIN_SEC do { \
            time_t t = time(NULL); \
            struct tm *tm_ptr = localtime(&t); \
            printf("%02d:%02d:%02d:", tm_ptr->tm_hour, tm_ptr->tm_min, tm_ptr->tm_sec);\
        } while (0);printf



#define SHM_NAME "/example_shm"
#define SHM_SIZE 1024

void parent_process();
void child_process();

int main() 
{
    pid_t pid = fork();
    if (pid == 0) 
    {
        child_process();
    } 
    else if (pid > 0) 
    {
        parent_process();
    } 
    else 
    {
        perror("fork failed");
        exit(EXIT_FAILURE);
    }
    return 0;
}

void parent_process() 
{
    PRINT_MIN_SEC("Parent start!\n");
    int shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
    if (shm_fd == -1) 
    {
        perror("shm_open failed");
        exit(EXIT_FAILURE);
    }

    ftruncate(shm_fd, SHM_SIZE);

    char *ptr = mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (ptr == MAP_FAILED) {
        perror("mmap failed");
        exit(EXIT_FAILURE);
    }

    bzero(ptr, SHM_SIZE);
    strcpy(ptr, "Parent Write Data");

    sleep(2); // 等待子进程读取共享内存中的数据

    PRINT_MIN_SEC("Parent read from shared memory: %s\n", ptr);
    munmap(ptr, SHM_SIZE);
    close(shm_fd);
    shm_unlink(SHM_NAME);
}

void child_process() 
{
    PRINT_MIN_SEC("Child start!\n");
    
    sleep(1); // 等待父进程创建共享内存并写入数据到共享内存
    
    int shm_fd = shm_open(SHM_NAME, O_RDWR, 0666);
    if (shm_fd == -1) 
    {
        perror("shm_open failed");
        exit(EXIT_FAILURE);
    }

    char *ptr = mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (ptr == MAP_FAILED) 
    {
        perror("mmap failed");
        exit(EXIT_FAILURE);
    }

    PRINT_MIN_SEC("Child read from shared memory: %s\n", ptr);
    bzero(ptr, SHM_SIZE);
    strcpy(ptr, "Child Write Data");

    munmap(ptr, SHM_SIZE);
    close(shm_fd);
}

通过父子间进程读写共享内存的时间不一致来实现进程通信,测试结果如下:

3.2 共享内存配合信号量使用测试

测试代码如下,代码编译需要加-lrt -pthread

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <semaphore.h>


// 打印时分秒的宏        
#define PRINT_MIN_SEC do { \
            time_t t = time(NULL); \
            struct tm *tm_ptr = localtime(&t); \
            printf("%02d:%02d:%02d:", tm_ptr->tm_hour, tm_ptr->tm_min, tm_ptr->tm_sec);\
        } while (0);printf



#define SHM_NAME "/example_shm"
#define SHM_SIZE 1024


void parent_process();
void child_process();

// 打印当前信号量的值
void printfValue(sem_t *sem)
{
    // 打印当前值
    int value;
    sem_getvalue(sem, &value);
    PRINT_MIN_SEC("Current value of semaphore: %d\n", value);
}


int main() 
{
    sem_t *mutex = sem_open("/mysemaphore", O_CREAT, 0644, 0);  
    
    pid_t pid = fork();
    if (pid == 0) 
    {
        child_process(mutex);
    } 
    else if (pid > 0) 
    {
        parent_process(mutex);
    } 
    else 
    {
        perror("fork failed");
        exit(EXIT_FAILURE);
    }
    return 0;
}

void parent_process(sem_t * mutex) 
{
    int SendNum = 0;
    char SendData[32] = {0};
    PRINT_MIN_SEC("Parent start!\n");
    printfValue(mutex);
    
    int shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
    if (shm_fd == -1) 
    {
        perror("shm_open failed");
        exit(EXIT_FAILURE);
    }

    ftruncate(shm_fd, SHM_SIZE);

    char *ptr = mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (ptr == MAP_FAILED) {
        perror("mmap failed");
        exit(EXIT_FAILURE);
    }

    while(1)
    {
        sprintf(SendData, "Parent Write Data-%d\n", SendNum);
        SendNum++;
        strcpy(ptr, SendData);
        sem_post(mutex);
        sleep(2);
    }


    PRINT_MIN_SEC("Parent read from shared memory: %s\n", ptr);

    munmap(ptr, SHM_SIZE);
    close(shm_fd);
    shm_unlink(SHM_NAME);
}

void child_process(sem_t * mutex) 
{
    PRINT_MIN_SEC("Child start!\n");
    printfValue(mutex);
    sleep(1); // 等待父进程创建共享内存并写入数据到共享内存
    
    int shm_fd = shm_open(SHM_NAME, O_RDWR, 0666);
    if (shm_fd == -1) 
    {
        perror("shm_open failed");
        exit(EXIT_FAILURE);
    }

    char *ptr = mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (ptr == MAP_FAILED) 
    {
        perror("mmap failed");
        exit(EXIT_FAILURE);
    }

    while(1)
    {
        sem_wait(mutex);
        PRINT_MIN_SEC("Child read from shared memory: %s\n", ptr);
    }

    munmap(ptr, SHM_SIZE);
    close(shm_fd);
}

父进程通过信号量,每1秒写一次数据,写入完毕后执行post,子进程一直阻塞等待,获取到信号量后从共享内存读取数据,测试结果如下:

4、总结

本文阐述了进程间通信之共享内存(POSIX)的定义、应用场景、优缺点等,列举了编程中使用的接口,编写了测试用例测试相关功能。

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

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

相关文章

Hive的Join连接

前言 Hive-3.1.2版本支持6种join语法。分别是&#xff1a;inner join&#xff08;内连接&#xff09;、left join&#xff08;左连接&#xff09;、right join&#xff08;右连接&#xff09;、full outer join&#xff08;全外连接&#xff09;、left semi join&#xff08;左…

C++ bfs再探迷宫游戏(五十五)【第二篇】

今天我们用bfs解决迷宫游戏。 1.再探迷宫游戏 前面我们已经接触过了迷宫游戏&#xff0c;并且学会了如何使用 DFS 来解决迷宫最短路问题。用 DFS 求解迷宫最短路有一个很大的缺点&#xff0c;需要枚举所有可能的路径&#xff0c;读入的地图一旦很大&#xff0c;可能的搜索方案…

VM和Linux安装

VM和Linux安装 一、下载VM 1.官网地址&#xff1a;https://www.vmware.com/cn.html 2.其他地址&#xff1a;http://ww7.nocmd.com/windows/740.html 许可证这个&#xff0c;大家可以自己上网搜索&#xff0c;很容易就搜索到就可以使用了 上面内容就是安装VM的步骤 安…

BFS与DFS初级练习(排列数字,n-皇后,走迷宫)

BFS与DFS初步了解 DFS&#xff08;深度优先搜索&#xff09;和BFS&#xff08;广度优先搜索&#xff09;是两种常用的图遍历算法。 DFS是一种递归的搜索算法&#xff0c;它从起始节点开始&#xff0c;沿着路径依次访问与当前节点相邻的未访问节点&#xff0c;直到无法继续访问…

Pytorch的可视化

1 使用 wandb进行可视化训练过程 本文章将从wandb的安装、wandb的使用、demo的演示进行讲解。 1.1 如何安装wandb&#xff1f; wandb的安装比较简单&#xff0c;在终端中执行如下的命令即可&#xff1a; pip install wandb在安装完成之后&#xff0c;我们需要&#xff0c;去…

【力扣】5.最长回文子串

这道题我主要是通过动态规划来进行解题&#xff0c;看了我好久&#xff08;解析&#xff09;&#xff0c;生疏了呀。 首先就是判断一个字符串是不是回文&#xff0c;我们可以设置两个指针&#xff0c;从前往后进行判断即可&#xff0c;运用暴力解题法&#xff0c;这里运用的动…

【C++】内存详解(堆,栈,静态区)

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

【FPGA】VHDL:八段码到8421BCD码转换电路

目录 EDA设计基础练习题 &#xff1a; 实验要求如下&#xff1a; 代码 八段码到8421BCD码转换电路 8421BCD码到八段码转换电路 八段码到8421BCD~运行结果展示 8421BCD转八段码~运行结果展示 特别注意 软件&#xff1a;Quartus II 13.0 (64-bit) 语言&#xff1a;VHDL E…

书生谱语-基于 InternLM 和 LangChain 搭建知识库

大语言模型与外挂知识库&#xff08;RAG&#xff09;的优缺点 RAG方案构建与优化 作业 在创建web_demo时&#xff0c;需要根据教程将服务器端口映射到本地端口&#xff0c;另外需要将链接的demo从服务器中复制出来&#xff0c;不要直接从服务器打开demo页面&#xff0c;不然会…

Blazor OIDC 单点登录授权实例5 - 独立SSR App (net8 webapp ) 端授权

目录: OpenID 与 OAuth2 基础知识Blazor wasm Google 登录Blazor wasm Gitee 码云登录Blazor OIDC 单点登录授权实例1-建立和配置IDS身份验证服务Blazor OIDC 单点登录授权实例2-登录信息组件wasmBlazor OIDC 单点登录授权实例3-服务端管理组件Blazor OIDC 单点登录授权实例4 …

低资源学习与知识图谱:构建与应用

目录 前言1 低资源学习方法1.1 数据增强1.2 特征增强1.3 模型增强 2 低资源知识图谱构建与推理2.1 元关系学习2.2 对抗学习2.3 零样本关系抽取2.4 零样本学习与迁移学习2.5 零样本学习与辅助信息 3 基于知识图谱的低资源学习应用3.1 零样本图像分类3.2 知识增强的零样本学习3.3…

鸿蒙小案例-你画我猜

鸿蒙小案例-你画我猜 1.准备组件(组件布局) 2.实现跟随鼠标画笔画出图案功能 3.实现复制上面的画笔的图案功能 4.其他小功能1.组件的准备 画布的组件官方给的API是Canvas&#xff0c;需要传递一个参数CanvasRenderingContext2D 直接搜索API 使用官方案例 private settings: …

【GO语言卵细胞级别教程】05.项目创建和函数讲解

感谢&#xff01;点点赞和评论呀&#xff01;我将继续更新 目录&#xff1a; 感谢&#xff01;点点赞和评论呀&#xff01;我将继续更新0.创建项目1.函数的引入2.注意事项3.详细介绍3.1 形参介绍 4.导入包4.1 基本知识4.2 注意事项 5.init函数6.匿名函数 0.创建项目 创建目录 …

力扣精选算法100道——矩阵区域和 (前缀和专题)

目录 &#x1f388;了解题意 &#x1f388;算法原理 &#x1f388;实现代码 &#x1f388;了解题意 给定一个大小为 m x n 的矩阵 mat 和一个整数 k&#xff0c;你需要计算一个新的矩阵 answer&#xff0c;其中每个 answer[i][j] 表示矩阵 mat 中以坐标 (i, j) 为中心、边…

JavaScript 事件循环:Event Loop

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;《爱蹦跶的大A阿》 &#x1f525;当前正在更新专栏&#xff1a;《VUE》 、《JavaScript保姆级教程》、《krpano》、《krpano中文文档》 ​ ​ ✨ 前言 事件循环 是 web 开发中的一个核心概念&#xff0c;它是 JavaScript…

【Linux】线程概念和线程控制

线程概念 一、理解线程1. Linux中的线程2. 重新定义线程和进程3. 进程地址空间之页表4. 线程和进程切换5. 线程的优点6. 线程的缺点7. 线程异常8. 线程用途9. 线程和进程 二、线程控制1. pthread 线程库&#xff08;1&#xff09;pthread_create()&#xff08;2&#xff09;pth…

[Doris] Doris的安装和部署 (二)

文章目录 1.安装要求1.1 Linux操作系统要求1.2 软件需求1.3 注意事项1.4 内部端口 2.集群部署2.1 操作系统安装要求2.2 下载安装包2.3 解压2.4 配置FE2.5 配置BE2.6 添加BE2.7 FE 扩容和缩容2.8 Doris 集群群起脚本 3.图形化 1.安装要求 1.1 Linux操作系统要求 1.2 软件需求 1…

Acwing---842.排列数字

排列数字 1.题目2.基本思想3.代码实现 1.题目 给定一个整数 n&#xff0c;将数字 1∼n排成一排&#xff0c;将会有很多种排列方法。 现在&#xff0c;请你按照字典序将所有的排列方法输出。 输入格式 共一行&#xff0c;包含一个整数 n。 输出格式 按字典序输出所有排列方案…

mysql Day05

sql性能分析 sql执行频率 show global status like Com_______ 慢查询日志 执行时间超过10秒的sql语句 profile详情 show profiles帮助我们了解时间都耗费到哪里了 #查看每一条sql的耗时情况 show profiles#查看指定query_id的sql语句各个阶段的耗时情况 show profile fo…

【电路笔记】-并联电感

并联电感 文章目录 并联电感1、概述2、并联电感示例13、互耦并联电感器4、并联电感示例25、并联电感示例36、总结当电感器的两个端子分别连接到另一个或多个电感器的每个端子时,电感器被称为并联连接在一起。 1、概述 所有并联电感器上的压降将是相同的。 然后,并联的电感器…