【嵌入式Linux应用开发基础】进程间通信(3):共享内存

news2025/2/21 12:34:07

目录

一、共享内存核心概念

1.1. 特点

1.2. 适用场景

二、共享内存原理剖析

三、嵌入式 Linux 中共享内存的使用

3.1. 相关函数介绍

3.2. System V共享内存操作步骤

3.3. 同步机制(示例使用System V信号量)

3.4. POSIX共享内存(现代替代方案)

四、关键注意事项

五、典型使用场景

5.1. 实时数据处理

5.2. 多任务协同

5.3. 资源受限系统

5.4. 高效数据交换

5.5. 特定应用场景下的需求

六、常见问题

6.1. 共享内存访问冲突

6.2. 共享内存泄漏

6.3. 共享内存大小不匹配

6.4. 共享内存访问权限问题

6.5. 共享内存映射失败

七、总结

八、参考资料


在嵌入式Linux应用开发中,共享内存是一种高效的进程间通信(IPC)方式,允许多个进程直接访问同一块内存区域,避免了数据复制的开销。

一、共享内存核心概念

1.1. 特点

  • 高效:数据无需在内核与用户空间间复制。

  • 直接访问:进程通过指针操作内存,速度快。

  • 无内置同步:需开发者自行处理进程间同步(如信号量)。

1.2. 适用场景

  • 频繁读写、大数据量传输(如音视频处理)。

  • 实时性要求高的嵌入式系统。

二、共享内存原理剖析

共享内存是一种进程间通信方式,它允许两个或多个进程访问同一块物理内存区域。这块共享的内存区域被映射到各个进程的地址空间中,使得进程可以直接对其进行读写操作,就如同访问自身的内存一样。这种直接访问的方式避免了数据在进程间的多次复制,大大提高了数据传输的速度,尤其适用于对数据传输效率要求较高的场景,如多媒体数据处理、实时数据采集与分析等。

与其他 IPC 机制(如管道、消息队列)相比,共享内存的优势在于其高效性。管道和消息队列在数据传递时需要进行数据的复制操作,而共享内存则是通过内存映射,让进程直接操作共享内存块,减少了数据复制带来的时间和空间开销。然而,这种高效性也带来了一些挑战,由于多个进程可以同时访问共享内存,需要采取额外的同步机制(如信号量、互斥锁)来确保数据的一致性和完整性,防止出现竞态条件

三、嵌入式 Linux 中共享内存的使用

3.1. 相关函数介绍

在嵌入式 Linux 中,使用共享内存需要借助一些系统调用函数,主要包括 shmgetshmatshmdt 和 shmctl

①shmget 函数

  • 功能:用于创建一个新的共享内存段或获取一个已存在的共享内存段的标识符。
  • 函数原型
    int shmget(key_t key, size_t size, int shmflg);

    参数说明

    • key:是一个整数值,用于唯一标识共享内存段。通常可以使用 ftok 函数生成一个 key 值。
    • size:指定共享内存段的大小,单位为字节。
    • shmflg:是一组标志位,用于指定共享内存的创建方式和权限。例如,IPC_CREAT 表示如果共享内存不存在则创建它,IPC_EXCL 与 IPC_CREAT 一起使用时,表示如果共享内存已存在则返回错误。
  • 返回值:成功时返回共享内存段的标识符,失败时返回 -1。

②shmat 函数

  • 功能:将共享内存段附加到调用进程的地址空间中,使得进程可以访问共享内存。
  • 函数原型
    void *shmat(int shmid, const void *shmaddr, int shmflg);
  • 参数说明
    • shmid:是 shmget 函数返回的共享内存段标识符。
    • shmaddr:指定共享内存段在进程地址空间中的映射地址。通常设置为 NULL,表示由系统自动选择合适的地址进行映射。
    • shmflg:是一组标志位,用于指定映射的方式。例如,SHM_RDONLY 表示以只读方式映射共享内存。
  • 返回值:成功时返回指向共享内存段的指针,失败时返回 (void *) -1

③shmdt 函数

  • 功能:将共享内存段从调用进程的地址空间中分离。
  • 函数原型
    int shmdt(const void *shmaddr);
  • 参数说明shmaddr 是 shmat 函数返回的指向共享内存段的指针。
  • 返回值:成功时返回 0,失败时返回 -1。

④shmctl 函数

  • 功能:用于控制共享内存段,如设置或获取共享内存段的属性、删除共享内存段等。
  • 函数原型
    int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • 参数说明
    • shmid:是共享内存段的标识符。
    • cmd:指定要执行的操作,如 IPC_STAT 用于获取共享内存段的状态信息,IPC_SET 用于设置共享内存段的属性,IPC_RMID 用于删除共享内存段。
    • buf:是一个指向 struct shmid_ds 结构体的指针,用于存储或传递共享内存段的相关信息。
  • 返回值:成功时返回 0,失败时返回 -1。

3.2. System V共享内存操作步骤

①创建/获取共享内存段

使用系统调用或库函数来创建一个共享内存段。在System V IPC机制中,可以使用shmget函数来创建或获取一个共享内存段。而在POSIX机制中,则可以使用mmap函数结合文件描述符来创建共享内存。

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

key_t key = ftok("/path/to/file", 'P'); // 生成唯一键值
int shmid = shmget(key, size, IPC_CREAT | 0666);
  • key:通过ftok生成或使用IPC_PRIVATE(仅限父子进程)。

  • size共享内存大小

  • 权限0666表示所有用户可读写。

②附加共享内存到进程空间

创建共享内存后,需要将其关联到进程的地址空间中。在System V IPC中,这可以通过shmat函数来实现。而在POSIX机制中,mmap函数本身就已经完成了内存映射的工作。

char *shm_ptr = (char*)shmat(shmid, NULL, 0);
if (shm_ptr == (void*)-1) {
    perror("shmat failed");
}

③读写数据

关联成功后,进程就可以像操作普通内存一样来访问共享内存了。需要注意的是,多个进程同时访问共享内存时,需要采取同步机制来避免竞态条件。直接通过指针操作:

sprintf(shm_ptr, "Hello from PID %d", getpid()); // 写入
printf("Received: %s\n", shm_ptr);              // 读取

④分离共享内存

当进程不再需要访问共享内存时,应该将其从地址空间中去除关联。在System V IPC中,可以通过shmdt函数来实现。

shmdt(shm_ptr);

⑤控制与删除

同时,如果共享内存段不再被任何进程使用,可以通过shmctl函数来删除它。而在POSIX机制中,则可以通过munmap函数来解除内存映射。

shmctl(shmid, IPC_RMID, NULL); // 标记删除(实际在所有进程分离后生效)

3.3. 同步机制(示例使用System V信号量)

#include <sys/sem.h>

// 创建信号量
int semid = semget(key, 1, IPC_CREAT | 0666);
semctl(semid, 0, SETVAL, 1); // 初始值1(互斥锁)

struct sembuf op;
op.sem_num = 0;
op.sem_flg = SEM_UNDO;

// P操作(加锁)
op.sem_op = -1;
semop(semid, &op, 1);

// V操作(解锁)
op.sem_op = 1;
semop(semid, &op, 1);

3.4. POSIX共享内存(现代替代方案)

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>

int fd = shm_open("/my_shm", O_CREAT | O_RDWR, 0666);
ftruncate(fd, size); // 设置大小

void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

// 使用后解除映射
munmap(ptr, size);
close(fd);
shm_unlink("/my_shm"); // 删除对象

四、关键注意事项

  • 同步问题:必须使用信号量或互斥锁避免数据竞争。

  • 生命周期:显式删除共享内存防止泄漏。

  • 一致性:确保进程间数据结构定义一致。

  • 错误处理:检查系统调用返回值,处理EACCESENOMEM等错误。

  • 资源限制:嵌入式系统中合理分配内存大小。

五、典型使用场景

5.1. 实时数据处理

共享内存特别适用于需要实时处理数据的场景,如音视频处理和传感器数据采集。在这些应用中,数据通常以高速率生成,并且需要被及时处理。通过共享内存,多个进程可以即时访问和更新数据,从而实现高效的实时数据处理。

5.2. 多任务协同

在嵌入式系统中,经常需要多个任务或进程协同工作以完成复杂的任务。共享内存提供了一种高效的方式来在这些任务或进程之间传递数据。例如,在多线程编程和进程间通信中,共享内存允许不同线程或进程访问同一段内存区域,从而简化了数据交换和同步的过程。

5.3. 资源受限系统

嵌入式设备和物联网设备通常具有有限的计算资源和存储空间。在这些资源受限的系统中,共享内存提供了一种有效的资源利用方式。通过允许多个进程共享同一段内存区域,可以减少内存的使用量,并降低系统的整体开销。

5.4. 高效数据交换

与其他IPC机制相比,共享内存具有更高的数据交换效率。因为进程可以直接读写内存,而不需要任何数据的拷贝(或者只需要很少的数据拷贝)。大大降低了数据交换的延迟和开销,使得共享内存成为高性能应用中的首选IPC方式。

5.5. 特定应用场景下的需求

在某些特定的应用场景下,如实时操作系统(RTOS)中的任务间通信、分布式系统中的节点间通信等,共享内存也发挥着重要的作用。这些场景通常对数据的实时性和一致性有很高的要求,而共享内存正好满足了这些需求。

六、常见问题

6.1. 共享内存访问冲突

问题描述:当多个进程同时访问共享内存区域时,如果没有适当的同步机制,可能会导致数据访问冲突,如数据不一致、数据丢失等问题。

解决方法

  • 使用信号量(Semaphore)进行同步:信号量是一种常用的进程间同步机制,可以用来控制对共享内存的访问。通过信号量的P(wait)和V(signal)操作,可以实现进程的互斥访问。
  • 使用互斥锁(Mutex):互斥锁也是一种有效的同步机制,它允许一个进程独占访问共享内存,直到该进程释放锁为止。

6.2. 共享内存泄漏

问题描述:共享内存不会在进程结束后自动删除,如果进程在退出前没有正确释放共享内存,就会导致内存泄漏。

解决方法

  • 在进程退出前使用shmdt()函数将共享内存与进程脱离,并使用shmctl()函数中的IPC_RMID命令删除共享内存段。
  • 确保在创建共享内存时设置了正确的权限和标志,以便在不再需要时能够顺利删除。

6.3. 共享内存大小不匹配

问题描述:如果两个进程试图访问同一个共享内存段,但其中一个进程在创建共享内存时指定的大小与另一个进程期望的大小不匹配,就会导致访问错误。

解决方法

  • 在创建共享内存时,确保所有进程都使用相同的大小参数。
  • 在访问共享内存之前,使用shmctl()函数查询共享内存的大小,以确保与期望的大小一致。

6.4. 共享内存访问权限问题

问题描述:如果共享内存的访问权限设置不当,可能会导致某些进程无法访问共享内存。

解决方法

  • 在创建共享内存时,确保设置了正确的权限标志。
  • 使用shmctl()函数修改共享内存的权限,以允许更多的进程访问。

6.5. 共享内存映射失败

问题描述:在将共享内存映射到进程地址空间时,可能会因为内存不足、地址空间冲突等原因导致映射失败。

解决方法

  • 确保系统有足够的内存资源来支持共享内存的映射。
  • 检查并避免地址空间冲突,确保没有其他进程或线程占用了相同的地址空间。
  • 使用mmap()函数进行匿名内存映射时,确保设置了正确的标志和参数。

七、总结

共享内存是嵌入式Linux应用开发中一种高效、快速的进程间通信机制。它允许多个进程访问同一块内存区域,从而实现了数据的高效共享。在使用共享内存时,需要注意同步机制、内存管理和安全性等问题。通过合理的使用共享内存,可以大大提高嵌入式Linux应用开发的效率和性能。

八、参考资料

  • 《从实践中学嵌入式 Linux 应用程序开发(第 2 版)》:由华清远见嵌入式学院的苗德行、冯建、刘洪涛、潘启勇著,电子工业出版社 2015 年 8 月出版。书中结合大量实例,讲解了嵌入式 Linux 应用程序设计各个方面的基本方法及必要的核心概念,包括嵌入式 Linux 进程间通信,重视应用是贯穿全书的最大特点。
  • 《嵌入式 Linux 应用开发完全手册》:对嵌入式 Linux 应用开发的各个方面进行了全面的介绍,其中关于进程间通信的章节会详细讲解共享内存的原理、使用方法以及相关的编程实例,帮助读者深入理解和掌握共享内存在嵌入式 Linux 中的应用。

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

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

相关文章

计算机毕业设计Python农产品推荐系统 农产品爬虫 农产品可视化 农产品大数据(源码+LW文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

「正版软件」PDF Reader - 专业 PDF 编辑阅读工具软件

PDF Reader 轻松查看、编辑、批注、转换、数字签名和管理 PDF 文件&#xff0c;以提高工作效率并充分利用 PDF 文档。 像专业人士一样编辑 PDF 编辑 PDF 文本 轻松添加、删除或修改 PDF 文档中的原始文本以更正错误。自定义文本属性&#xff0c;如颜色、字体大小、样式和粗细。…

日期类(完全讲解版)

1. 类的设计思想 Date 类的设计目的是为了封装和处理日期信息&#xff0c;它提供了对日期的基本操作&#xff0c;如日期加减、日期比较、日期合法性检查等。类中的私有成员 int _year, int _month, int _day 存储了日期的年、月、日。 类的声明和构造 Date 类的声明&#xff1…

洛谷 P10726 [GESP202406 八级] 空间跳跃 C++ 完整题解

一、题目链接 P10726 [GESP202406 八级] 空间跳跃 - 洛谷 二、解题思路 我们要对输入的挡板进行排序&#xff0c;按高度从高到低&#xff08;从小到大&#xff09;。 排序之后s和t都要更新。 struct Baffle {int l, r;int h;int id; } b[1005];void Sort() {sort(b 1, b 1 n…

【设计模式精讲】创建型模式之工厂方法模式(简单工厂、工厂方法)

文章目录 第四章 创建型模式4.2 工厂方法模式4.2.1 需求: 模拟发放奖品业务4.2.2 原始开发方式4.2.3 简单工厂模式4.2.3.1 简单工厂模式介绍4.2.3.2 简单工厂原理4.2.3.3 简单工厂模式重构代码4.2.3.4 简单工厂模式总结 4.2.4 工厂方法模式4.2.4.1 工厂方法模式介绍4.2.4.2 工厂…

【ROS2】【ROS2】RViz2源码分析(八):Display中订阅ROS2消息(使用Qt信号和槽传递ROS2消息)

1、简述 RViz2 涵盖了 Qt 和 ROS2 的技术点,前面介绍 DisplaysPanel 时,主要分析了Qt相关部分,参见博客: 【ROS2】RViz2源码分析(七):DisplaysPanel 中的树状模型/视图 本篇博客,将会一起学习 RViz2 中如何使用 ROS2,以 Display 中订阅 ROS2 消息为例。 2、通过话题…

牛顿法:用泰勒级数求解平方根的秘籍

目录 一、引言二、牛顿法的理论基础——泰勒级数三、牛顿法的原理与推导3.1 原理概述3.2 推导过程3.3 几何解释 四、牛顿法的应用场景4.1 数值计算4.2 优化问题 五、牛顿法求平方根的具体案例5.1 原理推导5.2 具体步骤5.3 代码实现&#xff08;Python&#xff09;5.4 示例计算过…

Unity 打开摄像头 并显示在UI

需求: 打开相机并显示在UI上 效果: 注意&#xff1a; 电脑可能有多个摄像头&#xff0c;注意名称 代码: using System; using System.Linq; using UnityEngine; using UnityEngine.UI; using System.Collections.Generic; #if UNITY_EDITOR using UnityEditor; #endifname…

RK Android11 WiFi模组 AIC8800 驱动移植流程

RK Android WiFi模组 AIC8800 驱动移植流程 作者&#xff1a;Witheart更新时间&#xff1a;20250220 概要&#xff1a;本文介绍了基于 AIC8800D40 芯片的 WiFi6 模组 BL-M8800DS2-40 在 RK3568 平台上的驱动移植流程。主要涉及环境搭建、驱动代码分析、设备树修改、驱动编译配…

Windows PyCharm的python项目移动存储位置后需要做的变更

项目使用的venv虚拟环境&#xff0c;因此项目移动存储位置后需要重新配置python解释器的位置&#xff0c;否则无法识别&#xff0c;若非虚拟环境中运行&#xff0c;则直接移动后打开即可&#xff0c;无需任何配置。 PyCharm版本为2021.3.3 (Professional Edition)&#xff0c;其…

浅棕色人像花卉照片Lr调色,手机滤镜PS+Lightroom预设下载!

调色介绍 提供一系列用于处理浅棕色调人像与花卉照片的后期预设资源&#xff0c;这些预设兼容手机滤镜的 PS 和 Lightroom 软件。其主要作用是令照片达成浅棕色的色调效果&#xff0c;帮助使用者快捷地对人像和花卉照片进行调色处理&#xff0c;无需繁复手动调节参数&#xff0…

POI pptx转图片

前言 ppt页面预览一直是个问题&#xff0c;office本身虽然有预览功能但是收费&#xff0c;一些开源的项目的预览又不太好用&#xff0c;例如开源的&#xff1a;kkfileview pptx转图片 1. 引入pom依赖 我这个项目比较老&#xff0c;使用版本较旧 <dependency><gro…

全志A133 android10 适配SLM770A 4G模块

一&#xff0c;模块基本信息 1.官方介绍 SLM770A是美格智能最新推出的一款LTE Cat.4无线通讯模组&#xff0c;最大支持下行速率150Mbps及上行速率50Mbps。同时向下兼容现有的3G和2G网络&#xff0c;以确保即使在偏远地区也可以进行网络通信。 SLM770A模组支持分集接收和MIMO技…

DP-最长上升子序列

题面&#xff1a; 样例&#xff1a; 思路&#xff1a; 遇到动态规划问题&#xff0c;我们照旧思考两部分&#xff0c;状态表示以及状态计算。这里我们f[N]表示以第i个数结尾的上升子序列的最大值。我们将f[N]划分为若干个部分&#xff0c;因为我们要用到递推思路想办法用前面的…

【C++第二十章】红黑树

【C第二十章】红黑树 红黑树介绍&#x1f9d0; 红黑树是一种自平衡的二叉搜索树&#xff0c;通过颜色标记和特定规则保持树的平衡性&#xff0c;从而在动态插入、删除等操作中维持较高的效率。它的最长路径不会超过最短路径的两倍&#xff0c;它的查找效率比AVL树更慢(对于CPU…

如何修改Windows系统Ollama模型存储位置

默认情况下&#xff0c;Ollama 模型会存储在 C 盘用户目录下的 .ollama/models 文件夹中&#xff0c;这会占用大量 C 盘空间&#xff0c;增加C盘“爆红”的几率。所以&#xff0c;我们就需要修改Ollama的模型存储位置 Ollama提供了一个环境变量参数可以修改Ollama的默认存在位…

OpenAI ChatGPT在心理治疗领域展现超凡同理心,通过图灵测试挑战人类专家

近期&#xff0c;一项关于OpenAI ChatGPT在心理治疗领域的研究更是引起了广泛关注。据报道&#xff0c;ChatGPT已经成功通过了治疗师领域的图灵测试&#xff0c;其表现甚至在某些方面超越了人类治疗师&#xff0c;尤其是在展现同理心方面&#xff0c;这一发现无疑为AI在心理健康…

Netflix Ribbon:云端负载均衡利器

Netflix Ribbon&#xff1a;云端负载均衡利器 ribbon Ribbon is a Inter Process Communication (remote procedure calls) library with built in software load balancers. The primary usage model involves REST calls with various serialization scheme support. 项目地…

【Android】Android 悬浮窗开发 ( 动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

文章目录 一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后返回处理 二、悬浮窗 前台服务和通知1、前台服务 启动 悬浮窗 的必要性① 保持悬浮窗存活② 悬浮窗的要求③ 悬浮窗版本兼容 2、其它类型服务简介① 前台服务…

Python高级语法之jsonpathBeautifulSoup解析器

目录&#xff1a; 1、jsonPath的使用2、使用jsonpath解析淘票票网页3、BeautifulSoup解析器的使用4、BeautifulSoup层级选择器的使用 1、jsonPath的使用 2、使用jsonpath解析淘票票网页 3、BeautifulSoup解析器的使用 4、BeautifulSoup层级选择器的使用