执行应用共享内存空间 同步QT进行快速捕获数据流

引言:本文章针对驱动的应用app,例如sensor data内容的获取,显示到QT的一种办法,共享内存。举例子,这是一个常见需求,比如摄像头采集进程与 GUI 显示进程分离,通过共享内存传输图像,避免 socket、pipe 等冗余复制。

进程A(数据采集者):采集图像数据(如 OpenCV),写入共享内存。
进程B(Qt GUI):读取共享内存内容并展示图像,避免拷贝。
使用 共享内存(QSharedMemory) 实现 高效通信
实现同步策略,如 信号量 / 标志位 / 双缓冲机制

那么笔者这边用的案例就是下述两个应用app,利用此demo获取数据流,实现题目目标要求,加上共享内存等机制,供读者观看,如下所示:

/*
*
*   file: dht11.c
*   date: 2024-08-06
*   usage: 
*       sudo gcc -o dht11 dht11.c -lrt
*       sudo ./dht11
*
*/#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <stdint.h>
#include <sys/mman.h>
#include <errno.h>#define DEV_NAME "/dev/dht11"// 定义共享内存结构
struct sensor_data {float temperature;float humidity;int is_valid;
};int main(int argc, char **argv)
{int fd;int ret;uint8_t data[6];int shm_fd;struct sensor_data *shm_data;/* 创建共享内存 */shm_fd = shm_open("/dht11_data", O_CREAT | O_RDWR, 0666);if (shm_fd < 0) {printf("创建共享内存失败: %s\n", strerror(errno));printf("错误代码: %d\n", errno);return -1;}printf("成功创建共享内存,fd: %d\n", shm_fd);/* 设置共享内存大小 */if (ftruncate(shm_fd, sizeof(struct sensor_data)) < 0) {printf("设置共享内存大小失败: %s\n", strerror(errno));printf("错误代码: %d\n", errno);close(shm_fd);return -1;}printf("成功设置共享内存大小: %ld\n", sizeof(struct sensor_data));/* 映射共享内存 */shm_data = (struct sensor_data *)mmap(NULL, sizeof(struct sensor_data),PROT_READ | PROT_WRITE, MAP_SHARED,shm_fd, 0);if (shm_data == MAP_FAILED) {printf("映射共享内存失败: %s\n", strerror(errno));printf("错误代码: %d\n", errno);close(shm_fd);return -1;}printf("成功映射共享内存,地址: %p\n", shm_data);/* 初始化共享内存数据 */shm_data->is_valid = 0;/* open dev */fd = open(DEV_NAME, O_RDWR);if (fd < 0) {printf("can not open file %s, %d\n", DEV_NAME, fd);munmap(shm_data, sizeof(struct sensor_data));close(shm_fd);return -1;}while(1) {/* read date from dht11 */ret = read(fd, &data, sizeof(data));	if(ret) {printf("Temperature=%d.%d Humidity=%d.%d\n", data[2], data[3], data[0], data[1]);/* 更新共享内存数据 */shm_data->temperature = (float)data[2] + (float)data[3]/10.0;shm_data->humidity = (float)data[0] + (float)data[1]/10.0;shm_data->is_valid = 1;} else {printf("error reading!\n");shm_data->is_valid = 0;}sleep(1);}/* 清理资源 */close(fd);munmap(shm_data, sizeof(struct sensor_data));close(shm_fd);return 0;
}

这段 dht11.c 是一个 Linux 下使用共享内存同步 DHT11 温湿度传感器数据 的完整示例,涵盖设备读取、共享内存映射与进程间通信的核心流程。

// 定义共享内存结构

sensor_data 是共享给其他进程的数据结构;

is_valid 表示当前温湿度值是否有效;

/dev/dht11 是 DHT11 驱动的设备文件节点。

shm_open 创建/打开名为 /dht11_data 的共享内存对象,权限为 0666(所有用户可读写)

.设置共享内存大小,以及简单的error报错判断了

设置共享内存大小等于 sensor_data 结构体大小(通常为 12字节),将共享内存映射到当前进程地址空间;之后即可通过 shm_data->temperature 等成员直接访问。

把读取结果写入共享内存,供其他程序访问。

总结app案例一:

“共享内存”是多个进程之间最快的数据通信机制。通过 shm_open 创建内核中的一个匿名对象,并映射(mmap)进用户空间,多个进程即可在不同地址空间访问同一块物理内存。”

DHT11芯片 通过 /dev/dht11 驱动 到 应用程序 A (写入) 再到 共享内存段 再到 应用程序 B (如 Qt GUI) 

创建共享内存对象(shm_open)

int shm_fd = shm_open("/dht11_data", O_CREAT | O_RDWR, 0666);
设置共享内存大小(ftruncate)

ftruncate(shm_fd, sizeof(struct sensor_data));

映射共享内存到本进程(mmap)

shm_data = (struct sensor_data *)mmap(NULL, sizeof(...),
                PROT_READ | PROT_WRITE, MAP_SHARED,
                shm_fd, 0);

写入数据(数据生产者)

shm_data->temperature = ...;
shm_data->humidity = ...;
shm_data->is_valid = 1;

另一个进程读取(数据消费者)

int shm_fd = shm_open("/dht11_data", O_RDONLY, 0666);
struct sensor_data* data = mmap(NULL, sizeof(...), PROT_READ, MAP_SHARED, shm_fd, 0);

if (data->is_valid) {
    printf("温度: %.1f°C, 湿度: %.1f%%\n", data->temperature, data->humidity);
}

再来一个应用app案例,如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <errno.h>
#include <string.h>/* 寄存器地址 */
#define SMPLRT_DIV 0x19
#define PWR_MGMT_1 0x6B
#define CONFIG 0x1A
#define ACCEL_CONFIG 0x1C
#define GYRO_CONFIG 0x1B#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define GYRO_XOUT_H 0x43
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48//从机地址 MPU6050 地址
#define Address 0x68// 定义共享内存结构
struct mpu_data {float accel_x;float accel_y;float accel_z;float gyro_x;float gyro_y;float gyro_z;int is_valid;
};//MPU6050 操作相关函数
static int mpu6050_init(int fd,uint8_t addr);
static int i2c_write(int fd, uint8_t addr,uint8_t reg,uint8_t val);
static int i2c_read(int fd, uint8_t addr,uint8_t reg,uint8_t * val);static short GetData(int fd,uint8_t addr,unsigned char REG_Address);int main(int argc, char *argv[]){int fd;int shm_fd;struct mpu_data *shm_data;char i2c_dev[20];if(argc < 2){printf("使用错误!\n");printf("用法: %s [设备]\n", argv[0]);return -1;}strcpy(i2c_dev, argv[1]);/* 创建共享内存 */shm_fd = shm_open("/mpu6050_data", O_CREAT | O_RDWR, 0666);if (shm_fd < 0) {printf("创建共享内存失败: %s\n", strerror(errno));printf("错误代码: %d\n", errno);return -1;}printf("成功创建共享内存,fd: %d\n", shm_fd);/* 设置共享内存大小 */if (ftruncate(shm_fd, sizeof(struct mpu_data)) < 0) {printf("设置共享内存大小失败: %s\n", strerror(errno));printf("错误代码: %d\n", errno);close(shm_fd);return -1;}printf("成功设置共享内存大小: %ld\n", sizeof(struct mpu_data));/* 映射共享内存 */shm_data = (struct mpu_data *)mmap(NULL, sizeof(struct mpu_data),PROT_READ | PROT_WRITE, MAP_SHARED,shm_fd, 0);if (shm_data == MAP_FAILED) {printf("映射共享内存失败: %s\n", strerror(errno));printf("错误代码: %d\n", errno);close(shm_fd);return -1;}printf("成功映射共享内存,地址: %p\n", shm_data);/* 初始化共享内存数据 */shm_data->is_valid = 0;fd = open(argv[1], O_RDWR);if (fd < 0) {printf("无法打开设备 %s: %s\n", argv[1], strerror(errno));munmap(shm_data, sizeof(struct mpu_data));close(shm_fd);return -1;}//初始化 MPU6050if (mpu6050_init(fd,Address) < 0) {printf("MPU6050初始化失败\n");close(fd);munmap(shm_data, sizeof(struct mpu_data));close(shm_fd);return -1;}while(1){usleep(1000 * 10);shm_data->accel_x = GetData(fd,Address,ACCEL_XOUT_H);usleep(1000 * 10);shm_data->accel_y = GetData(fd,Address,ACCEL_YOUT_H);usleep(1000 * 10);shm_data->accel_z = GetData(fd,Address,ACCEL_ZOUT_H);usleep(1000 * 10);shm_data->gyro_x = GetData(fd,Address,GYRO_XOUT_H);usleep(1000 * 10);shm_data->gyro_y = GetData(fd,Address,GYRO_YOUT_H);usleep(1000 * 10);shm_data->gyro_z = GetData(fd,Address,GYRO_ZOUT_H);shm_data->is_valid = 1;printf("加速度 X:%6d Y:%6d Z:%6d\n", shm_data->accel_x, shm_data->accel_y, shm_data->accel_z);printf("陀螺仪 X:%6d Y:%6d Z:%6d\n\n", shm_data->gyro_x, shm_data->gyro_y, shm_data->gyro_z);sleep(1);}close(fd);munmap(shm_data, sizeof(struct mpu_data));close(shm_fd);return 0;}static int mpu6050_init(int fd,uint8_t addr){i2c_write(fd, addr,PWR_MGMT_1,0x00); //配置电源管理,0x00, 正常启动i2c_write(fd, addr,SMPLRT_DIV,0x07); //设置 MPU6050 的输出分频既设置采样i2c_write(fd, addr,CONFIG,0x06); //配置数字低通滤波器和帧同步引脚i2c_write(fd, addr,ACCEL_CONFIG,0x01); //设置量程和 X、Y、Z 轴加速度自检return 0;}static int i2c_write(int fd, uint8_t addr,uint8_t reg,uint8_t val){int retries;uint8_t data[2];data[0] = reg;data[1] = val;//设置地址长度:0 为 7 位地址ioctl(fd,I2C_TENBIT,0);
//设置从机地址if (ioctl(fd,I2C_SLAVE,addr) < 0){printf("fail to set i2c device slave address!\n");close(fd);return -1;}//设置收不到 ACK 时的重试次数ioctl(fd,I2C_RETRIES,5);if (write(fd, data, 2) == 2){return 0;}else{return -1;}}static int i2c_read(int fd, uint8_t addr,uint8_t reg,uint8_t * val){int retries;//设置地址长度:0 为 7 位地址ioctl(fd,I2C_TENBIT,0);//设置从机地址if (ioctl(fd,I2C_SLAVE,addr) < 0){printf("fail to set i2c device slave address!\n");close(fd);return -1;}
//设置收不到 ACK 时的重试次数ioctl(fd,I2C_RETRIES,5);if (write(fd, &reg, 1) == 1){if (read(fd, val, 1) == 1){return 0;}}else{return -1;}}static short GetData(int fd,uint8_t addr,unsigned char REG_Address){char H, L;i2c_read(fd, addr,REG_Address, &H);usleep(1000);i2c_read(fd, addr,REG_Address + 1, &L);return (H << 8) +L;
}

简单的IIC应用,不过多提了,重点也是数据落达的问题,内存共享,

加速度 (X/Y/Z)

陀螺仪 (X/Y/Z)

is_valid 是一个标志,说明该结构数据是否有效(是否已经写入)。

这里也是创建共享内存的对象,文件描述符。

设置共享内存的大小

映射

赋值

跟刚刚的流程一摸一样,因此现在数据落达内存区域的链路通了,现在就是QT等gui应用去读取捕获这些数据内容的过程了,如下所示:

初始化,你想数据获取就先创建共享内存空间索引,如下所示:

打开或创建共享内存对象

设置共享内存大小

映射共享内存到用户空间

初始化结构体有效标志

之后就是定时器反复读取了。

共享内存中的数据是通过两个指针 mpu_datadht11_data 来访问的,而这两个指针指向的是映射到共享内存的结构体。

[Producer进程]
  └─> shm_open("/mpu6050_data") + ftruncate()
  └─> mmap() 得到指针
  └─> 周期性更新 mpu_data 的内容 + is_valid=1

[Qt消费者]
  └─> shm_open("/mpu6050_data")
  └─> mmap() 得到 mpu_data 指针
  └─> 周期性读取 mpu_data,更新 QLabel

最后笔者再通过应用补充一个知识点。

共享内存(POSIX shm_open + mmap)的底层原理其实是:

1. 内核的内存管理子系统里

共享内存对象 /mpu6050_data/dht11_data 并不是存在某个具体的物理文件上;

它们是内核维护的一段物理内存页(page frames),被映射进进程虚拟地址空间;

这段物理内存区域驻留在 RAM(内存) 中,不在磁盘上。

2. 具体来说,RK3566这类基于Linux的SoC:

RK3566运行的是Linux内核,POSIX共享内存由内核虚拟文件系统(通常是 tmpfs 类型)支持;

共享内存对象表现为 /dev/shm 下的文件(你可以用 ls /dev/shm 查看);

这个目录本身挂载在 tmpfs 上,tmpfs 是基于内存(RAM)的文件系统;

数据实际就在RAM里,并不占用实际磁盘空间

层次说明
进程虚拟内存你程序中通过 mmap 得到的指针
内核内存管理内核分配的物理内存页
tmpfs文件系统/dev/shm/ 里的文件对应的内存
物理设备RK3566上的物理RAM

3. 如何验证?

运行 mount | grep shm,一般会看到 /dev/shm 挂载的 tmpfs

ls /dev/shm 可以看到共享内存名字(如果进程打开过);

这些文件大小对应你的 ftruncate 设置大小;

数据写入这些“文件”,实质是写入内核分配的RAM页。

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

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

相关文章

opencl的简单介绍以及c++实例

&#x1f9e9; 一、什么是 OpenCL&#xff1f; OpenCL&#xff08;Open Computing Language&#xff09; 是一个用于异构计算的开放标准&#xff0c;由 Khronos Group 提出和维护。它允许你在各种计算设备上&#xff08;如 CPU、GPU、DSP、FPGA&#xff09;并行运行代码&#…

ThingsCloud事物云平台搭建-微信小程序

ThingsCloud云平台与微信小程序设计 本文主要是介绍ThingsCloud云平台的搭建及微信小程序与app的使用。 当前文章是作为一个通用案例,介绍如何快速使用 ThingsCloud云平台 以及 利用 ThingsCloud云平台平台的框架快速设计手机APP和微信小程序。 可以快速让硬件接入,实现硬件…

2024 一带一路暨金砖国家职业技能大赛(金砖国家未来技能和技术挑战赛)

2024 一带一路暨金砖国家职业技能大赛&#xff08;金砖国家未来技能和技术挑战赛任务书&#xff09; 1 参加比赛的形式&#xff1a;2 项目阶段简介&#xff1a;3 项目阶段和所需时间&#xff1a;4 第一阶段&#xff1a;职业素养与理论技能4.1 项目 1.职业素养4.2 项目 2.法律法…

2025-06-13【api】阿里百炼api调用方法

通过调用各种大模型可以完成对文生文&#xff0c;文生图&#xff0c;图片理解&#xff0c;文生视频&#xff0c;音频识别&#xff0c;文转音频等需求。 #方法一 import os from openai import OpenAI# 初始化客户端 client OpenAI(api_keyos.getenv("DASHSCOPE_API_KEY&…

软件工程的软件生命周期通常分为以下主要阶段

软件工程的软件生命周期通常分为以下主要阶段&#xff1a; 可行性分析 &#xff1a;评估项目的技术、经济、操作和法律可行性&#xff0c;确定项目是否值得开发。需求分析 &#xff1a;明确用户需求&#xff0c;定义软件功能和非功能需求&#xff0c;形成需求规格说明书。系统…

Spring依赖注入的四种方式(面)

目录 1. 构造器注入 2. 字段注入 3. Setter注入 4. 方法注入 最佳实践建议 1. 构造器注入 Service public class UserService {private final UserRepository userRepository;Autowired // Spring 4.3 可以省略public UserService(UserRepository userRepository) {this.…

通信网络编程2.0——JAVA

一、传统阻塞式 I/O 模型 实现简易多人聊天系统&#xff1a;服务端与客户端 服务端 public class ChatServer {int port 6666;// 定义服务器端口号为 6666ServerSocket ss;// 定义一个 ServerSocket 对象用于监听客户端连接//List<Socket> clientSockets new ArrayL…

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …

Python打卡第51天

浙大疏锦行 作业&#xff1a; day43的时候我们安排大家对自己找的数据集用简单cnn训练&#xff0c;现在可以尝试下借助这几天的知识来实现精度的进一步提高 import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from tor…

Notepad++ 官方下载

https://notepad-plus-plus.org/downloads/ 下载官网 1、https://github.com/notepad-plus-plus/notepad-plus-plus/releases 2、https://notepad-plus-plus.org/news/v881-we-are-with-ukraine/

运维之十个问题--2

目录 1. 如果有ip恶意刷流量怎么办 2. 标准端口范围 3.内存16G&#xff0c;交换分区多大 4.请简述非对称加密算法&#xff0c;ping命令通过什么协议实现&#xff0c;icmp是什么协议 5.客户访问网站速度慢原因 6. 进程和线程的区别 7.zabbix监控是你搭建的吗&#xff0c;平…

vue前端面试题——记录一次面试当中遇到的题(1)

1.v-if和v-show的区别 v-if和v-show都是Vue中用于条件渲染的指令&#xff0c;但它们的实现机制和适用场景有所不同&#xff1a; v-if是真正的条件渲染&#xff0c;在条件切换时会销毁和重建DOM元素&#xff0c;适合运行时条件变化不频繁的场景&#xff1b; v-show只是通过CS…

【QT面试题】(三)

文章目录 Qt信号槽的优点及缺点Qt中的文件流和数据流区别&#xff1f;Qt中show和exec区别QT多线程使用的方法 (4种)QString与基本数据类型如何转换&#xff1f;QT保证多线程安全事件与信号的区别connect函数的连接方式&#xff1f;信号与槽的多种用法Qt的事件过滤器有哪些同步和…

Vscode下Go语言环境配置

前言 本文介绍了vscode下Go语言开发环境的快速配置&#xff0c;为新手小白快速上手Go语言提供帮助。 1.下载官方Vscode 这步比较基础&#xff0c;已经安装好的同学可以直接快进到第二步 官方安装包地址&#xff1a;https://code.visualstudio.com/ 双击一直点击下一步即可,记…

HTML 文本省略号

目录 HTML 文本省略号超行省略号如何实现1. 单行文本溢出显示省略号2. 多行文本溢出显示省略号方法一&#xff1a;使用 -webkit-line-clamp&#xff08;推荐&#xff09;方法二&#xff1a;使用伪元素&#xff08;兼容性好&#xff09;方法三&#xff1a;使用 JavaScript 动态监…

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…

ffmpeg 新版本转码设置帧率上限

ffmpeg 新版本转码设置帧率上限 ffmpeg 在老版本比如 4.3的时候&#xff0c;转码设置帧率上限是通过vsync控制 # 设置动态控制最大帧率60 "-vsync 2 -r 60" 新版本这个参数没办法动态判断控制帧率了 替换为使用filter中的fps进行设置 # 设置动态帧率最大60帧 -…

Qt绘制电池图标源码分享

一、效果展示 二、源码分享 cell.h #ifndef CELL_WIDGET_H #define CELL_WIDGET_H #include <QWidget> #include <QPainter> #include <QPaintEngine> #include <QPaintEvent>/* 电池控件类 */ class CellWidget : public QWidget {Q_OBJECTQ_PROPERTY…

安卓基础(生成APK)

​​生成调试版&#xff08;Debug&#xff09;​​ Build → Build Bundle(s)/APK(s) → Build APK输出路径&#xff1a;app/build/outputs/apk/debug/app-debug.apk ​​生成发布版&#xff08;Release&#xff09;​​ Build → Generate Signed Bundle/APK → 选择 ​​APK​…

如何在 TypeScript 中使用类型保护

前言 类型保护是一种 TypeScript 技术&#xff0c;用于获取变量类型的信息&#xff0c;通常用于条件块中。类型保护是返回布尔值的常规函数​​&#xff0c;它接受一个类型并告知 TypeScript 是否可以将其缩小到更具体的值。类型保护具有独特的属性&#xff0c;可以根据返回的…