当前位置: 首页 > article >正文

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

目录

一、共享内存核心概念

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 中的应用。

相关文章:

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

目录 一、共享内存核心概念 1.1. 特点 1.2. 适用场景 二、共享内存原理剖析 三、嵌入式 Linux 中共享内存的使用 3.1. 相关函数介绍 3.2. System V共享内存操作步骤 3.3. 同步机制&#xff08;示例使用System V信号量&#xff09; 3.4. POSIX共享内存&#xff08;现代…...

日语学习-日语知识点小记-构建基础-JLPT-N4N5阶段(10): になります :表示从一种状态转变为另一种状态,“变得……”“成为……”

日语学习-日语知识点小记-构建基础-JLPT-N4&N5阶段(10): になります :表示从一种状态转变为另一种状态,“变得……”“成为……”  1、前言(1)情况说明(2)工程师的信仰2、知识点(1)寒くなりました & 元気になりました(2)何(なに)になりたいですか。(…...

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

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

Python HTTP 请求工具类 HttpUtils:简化 HTTP 请求的高效工具

在现代的 Web 开发和 API 集成中&#xff0c;HTTP 请求是最常见的操作之一。无论是获取数据、提交表单&#xff0c;还是与 RESTful API 交互&#xff0c;我们都需要频繁地发送 HTTP 请求。为了简化这些操作&#xff0c;提升代码的可读性和可维护性&#xff0c;我们可以使用一个…...

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

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

【linux】文件与目录命令 - grep

文章目录 1. 基本用法2. 常用参数3. 用法举例4. 注意事项5. 正则表达式5.1 正则表达式元字符5.2 正则表达式常用修饰符5.3 正则表达式运算符优先级5.4 正则表达式匹配规则 grep 命令用于在文本文件或标准输入中查找匹配的字符串模式&#xff0c;是一种功能强大的文本搜索工具&a…...

2025鸿蒙开发面试题汇总——通俗易懂

问题和通俗易懂的答案&#xff0c;覆盖鸿蒙开发的核心知识点和实际场景&#xff0c;方便面试时快速评估候选人能力&#xff1a; 一、基础概念&#xff08;必问&#xff09; 鸿蒙和安卓最大的区别是什么&#xff1f;举个实际例子。 答案&#xff1a;鸿蒙是“分布式操作系统”&am…...

专利申请流程详解:从创意到授权的完整指南

引言 专利是保护发明创造的重要法律工具&#xff0c;能够为发明人提供独占权&#xff0c;防止他人未经许可使用、制造或销售其发明。无论是个人发明者还是企业&#xff0c;了解专利申请流程都是至关重要的。本文将详细介绍专利申请的完整流程&#xff0c;帮助你从创意到授权&a…...

编写测试计划的六大要素是什么

编写测试计划时&#xff0c;一般需要包括以下六大要素&#xff0c;确保测试工作的完整性和可执行性&#xff1a; 1. 测试目标&#xff08;Test Objectives&#xff09; 明确测试的目的&#xff0c;如&#xff1a; 确保系统符合业务需求和技术要求。发现并修复软件缺陷。验证性…...

日期类(完全讲解版)

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

Python网络爬虫技术详解文档

Python网络爬虫技术详解文档 目录 网络爬虫概述爬虫核心技术解析常用Python爬虫库实战案例演示反爬虫机制与应对策略爬虫法律与道德规范高级爬虫技术资源推荐与学习路径1. 网络爬虫概述 1.1 什么是网络爬虫 网络爬虫(Web Crawler)是一种按特定规则自动抓取互联网信息的程序…...

解决服务器上运行YOLO时字体问题

问题描述:运行YOLO时&#xff0c;需要Arial.ttf&#xff0c;由于网络较差&#xff0c;可以先将字体下载到本地&#xff0c;再上传到服务器。 下载地址:项目首页 - Arial字体资源文件下载:本仓库提供了一系列Arial字体的ttf格式文件下载。Arial字体是一种广泛使用的无衬线字体&…...

洛谷 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…...

DeepSeek系列模型发展:从LLM到V3、R1的技术突破与优化各阶段的重要论文汇总(附下载地址)

DeepSeek 系列模型从最初的 LLM 版本发展到最新的 V3 和 R1 版本&#xff0c;在架构设计、训练效率和推理能力方面不断取得进步。以下是各版本按时间倒序的详细信息&#xff1a; 1. DeepSeek-R1 发布时间&#xff1a;2025年1月 论文标题&#xff1a;DeepSeek-R1: Incentivizi…...

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

文章目录 第四章 创建型模式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 工厂…...

AI大模型-提示工程学习笔记13-自动推理并使用工具

卷首语&#xff1a;我所知的是我自己非常无知&#xff0c;所以我要不断学习。 写给AI入行比较晚的小白们&#xff08;比如我自己&#xff09;看的&#xff0c;大神可以直接路过无视了。 自动推理并使用工具 (ART) 是一种结合了大语言模型&#xff08;LLM&#xff09;的推理能…...

【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…...

Linux系统中常见的词GNU是什么意思?

GNU 是 “GNU’s Not Unix” 的递归缩写&#xff0c;它是一个自由软件项目&#xff0c;旨在创建一个完全自由的操作系统。这个名字反映了GNU项目的核心理念&#xff1a;它试图创建一个类Unix的系统&#xff0c;但不是Unix本身。 GNU 项目由 理查德斯托曼&#xff08;Richard S…...

RAGFLOW使用flask转发的open ai接口

flask转发openai标准接口 背景 搭建RAGFLOW 的过程中&#xff0c;遇到一个比较严重的问题&#xff0c;公司部署的大模型代理需要获取token&#xff0c;且token存在有效期5分钟&#xff0c;在RAGFLOW中不能直接用&#xff0c;所以希望通过flask项目转发请求。 方案 比较好的…...

jQuery UI CSS 框架 API

jQuery UI CSS 框架 API 概述 jQuery UI 是一个基于 jQuery 的用户界面和交互库,它提供了一套丰富的交互组件和视觉效果,旨在帮助开发者快速构建具有吸引力和互动性的网页应用。jQuery UI CSS 框架 API 是 jQuery UI 的一部分,它允许开发者通过简单的 CSS 类来控制 UI 组件…...

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

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

【大模型】DeepSeek-RAG 本地化部署与军事情报应用研究报告

【大模型】DeepSeek-RAG 本地化部署与军事情报应用研究报告 一、研究背景二、DeepSeek 本地部署&#xff08;一&#xff09;部署环境&#xff08;二&#xff09;部署步骤&#xff08;三&#xff09;本地化部署流程优化 三、RAG 知识库构建&#xff08;一&#xff09;数据预处理…...

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…...

使用 Python 和 OpenCV 从一组图片生成 MP4 格式的视频

概要 在创建动画、制作幻灯片&#xff0c;从生成的图像数据中导出动态视频时&#xff0c;我们需要将一系列静态图片合成一个视频。 安装依赖 代码需要安装 OpenCV 库。可以通过命令行安装&#xff1a; pip install opencv-python 完整代码 图片尺寸不一时见后文 调整视频…...

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技…...

同步异步日志系统-设计模式

六大原则 单⼀职责原则&#xff08;Single Responsibility Principle&#xff09; 类的职责应该单⼀&#xff0c;⼀个⽅法只做⼀件事。职责划分清晰了&#xff0c;每次改动到最⼩单位的⽅法或 类。 使⽤建议&#xff1a;两个完全不⼀样的功能不应该放⼀个类中&#xff0c;⼀…...