執行應用共享內存空間 同步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
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/84548.shtml
英文地址,請注明出處:http://en.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;可以根據返回的…