Qt 监控USB设备的插入和移除

Qt 监控USB设备的插入和移除

flyfish

Ubuntu22.04
Qt 6.2.4

CMakeLists.txt 内容

# 指定 CMake 的最低版本要求
cmake_minimum_required(VERSION 3.16)# 定义项目的名称和使用的编程语言
project(USBMonitor LANGUAGES CXX)# 开启自动 UIC,MOC 和 RCC 工具
set(CMAKE_AUTOUIC ON)       # 自动运行 uic 工具处理 .ui 文件
set(CMAKE_AUTOMOC ON)       # 自动运行 moc 工具处理 Qt 的元对象系统
set(CMAKE_AUTORCC ON)       # 自动运行 rcc 工具处理资源文件# 设置 C++ 标准为 C++17,并且要求编译器必须支持 C++17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)# 查找 Qt 库,并指定所需的组件
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core libudev)  # 尝试查找 Qt6 或 Qt5 库,并且要求 Core 和 libudev 组件
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)     # 根据找到的 Qt 版本(Qt6 或 Qt5),再次查找 Core 组件# 包含 GNUInstallDirs 模块,该模块定义了一些标准的安装目录变量
include(GNUInstallDirs)# 定义一个可执行目标 USBMonitor,并指定其源文件为 main.cpp
add_executable(USBMonitormain.cpp
)# 将 Qt Core 库和 udev 库链接到 USBMonitor 可执行文件
target_link_libraries(USBMonitor PRIVATE Qt${QT_VERSION_MAJOR}::Core udev)# 定义安装目标 USBMonitor 的安装路径
install(TARGETS USBMonitorLIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}  # 指定库文件的安装路径RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}  # 指定可执行文件的安装路径
)

编译过程中的内部操作

  1. 解析 CMakeLists.txt

    • CMake 读取 CMakeLists.txt 文件,解析其中的指令和变量设置。
  2. 查找和配置依赖库

    • find_package 命令会查找 Qt 库和 libudev 库,并设置相应的变量,如 Qt6::CoreUDEV_LIBRARIES
  3. 生成构建文件

    • CMake 生成适合特定构建系统的文件,如 Makefile(对于 Unix 系统)或 Visual Studio 项目文件(对于 Windows 系统)。
  4. 编译源文件

    • 构建系统(如 make)根据生成的构建文件编译源文件 main.cpp,生成目标文件 main.cpp.o
  5. 链接目标文件

    • 构建系统将目标文件 main.cpp.oQt6::Coreudev 库链接,生成最终的可执行文件 USBMonitor
  6. 安装目标(可选):

    • 如果运行 make install 命令,构建系统会将生成的可执行文件 USBMonitor 安装到指定的目录,如 /usr/local/bin

源码

#include <QCoreApplication>  // 包含 Qt 核心模块
#include <QSocketNotifier>   // 包含 QSocketNotifier 类
#include <QDebug>            // 包含调试输出功能
#include <unistd.h>          // 包含 POSIX 操作系统 API
#include <fcntl.h>           // 包含文件控制选项
#include <libudev.h>         // 包含 libudev 库
#include <sys/inotify.h>     // 包含 inotify API
#include <errno.h>           // 包含错误码定义// 定义 UsbMonitor 类,继承自 QObject
class UsbMonitor : public QObject {Q_OBJECT  // 宏,用于 Qt 元对象系统public:// 构造函数UsbMonitor(QObject *parent = nullptr) : QObject(parent) {// 初始化 udevudev = udev_new();  // 创建 udev 上下文if (!udev) {qCritical() << "无法初始化 udev";  // 如果初始化失败,输出错误信息return;}// 创建 udev 监视器udev_monitor = udev_monitor_new_from_netlink(udev, "udev");  // 创建 udev 监视器if (!udev_monitor) {qCritical() << "无法创建 udev 监视器";  // 如果创建失败,输出错误信息return;}// 过滤只监听 USB 设备udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "usb", NULL);  // 过滤只监听 USB 子系统的设备udev_monitor_enable_receiving(udev_monitor);  // 启用接收事件// 获取 udev 监视器的文件描述符int udev_fd = udev_monitor_get_fd(udev_monitor);  // 获取 udev 监视器的文件描述符// 检查文件描述符是否有效if (fcntl(udev_fd, F_GETFD) == -1) {  // 使用 fcntl 检查文件描述符是否有效qCritical() << "无效的文件描述符:" << udev_fd;  // 如果无效,输出错误信息return;}// 创建 QSocketNotifier 并连接信号notifier = new QSocketNotifier(udev_fd, QSocketNotifier::Read, this);  // 创建 QSocketNotifier 对象connect(notifier, &QSocketNotifier::activated, this, &UsbMonitor::onUdevEvent);  // 连接 QSocketNotifier 的 activated 信号到 onUdevEvent 槽函数}// 析构函数~UsbMonitor() {if (notifier) {delete notifier;  // 释放 QSocketNotifier 对象}if (udev_monitor) {udev_monitor_unref(udev_monitor);  // 释放 udev 监视器}if (udev) {udev_unref(udev);  // 释放 udev 上下文}}private slots:// 处理 udev 事件的槽函数void onUdevEvent(int socket) {// 从 udev 监视器读取事件struct udev_device *dev = udev_monitor_receive_device(udev_monitor);  // 从 udev 监视器读取设备事件if (dev) {const char *action = udev_device_get_action(dev);  // 获取事件动作const char *devnode = udev_device_get_devnode(dev);  // 获取设备节点const char *devtype = udev_device_get_devtype(dev);  // 获取设备类型if (action && devnode && devtype) {if (strcmp(action, "add") == 0) {qDebug() << "USB 设备插入:" << devnode;  // 如果是插入事件,输出设备节点} else if (strcmp(action, "remove") == 0) {qDebug() << "USB 设备移除:" << devnode;  // 如果是移除事件,输出设备节点}}udev_device_unref(dev);  // 释放 udev_device 对象}}private:struct udev *udev;  // udev 上下文struct udev_monitor *udev_monitor;  // udev 监视器QSocketNotifier *notifier;  // QSocketNotifier 对象
};// 主函数
int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);  // 创建 QCoreApplication 对象UsbMonitor monitor;  // 创建 UsbMonitor 对象return a.exec();  // 进入事件循环
}#include "main.moc"  // 包含 moc 生成的代码

运行结果

USB 设备移除: /dev/bus/usb/002/006
USB 设备插入: /dev/bus/usb/002/007
USB 设备移除: /dev/bus/usb/002/007
USB 设备插入: /dev/bus/usb/002/008

#include "main.moc" 的主要作用是确保 moc(Meta-Object Compiler)生成的代码能够被编译器正确处理。具体来说,moc 会生成一些额外的代码,这些代码需要被包含在源文件的末尾,以便在编译时能够正确链接和使用。

Qt 元对象系统:

Qt 的元对象系统提供了信号和槽机制、运行时类型信息(RTTI)、动态属性系统等功能。
为了实现这些功能,Qt 提供了一个工具 moc(Meta-Object Compiler),它会生成一些额外的 C++ 代码。

moc 生成的代码:

当在类中使用 Q_OBJECT 宏时,moc 会为该类生成一些额外的代码,这些代码包括信号和槽的实现、元对象数据等。
生成的代码通常保存在一个与源文件同名的 .moc 文件中,例如 main.cpp 对应的 main.moc。

包含 main.moc:

为了确保生成的代码能够被编译器正确处理,需要在源文件的末尾包含 main.moc 文件。
这样做是为了确保 moc 生成的代码在编译时能够被正确链接到的源文件中。

编译顺序:

编译器在处理源文件时,会按照文件中出现的顺序依次处理每一行代码。
如果在源文件的开头包含 main.moc,可能会导致编译器在处理 moc 生成的代码时,还没有看到类的定义,从而引发编译错误。
因此,通常在源文件的末尾包含 main.moc,确保类的定义已经完全可见。

libudev 库提供的函数 ,用于管理和监控硬件设备的变化。

1. udev_new()

  • 原型struct udev *udev_new(void);
  • 作用:创建一个新的 udev 上下文。
  • 返回值:成功时返回指向 udev 结构的指针,失败时返回 NULL
  • 示例
    udev = udev_new();
    if (!udev) {qCritical() << "无法初始化 udev";return;
    }
    

2. udev_monitor_new_from_netlink()

  • 原型struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name);
  • 作用:创建一个新的 udev 监视器,用于监听来自内核的 netlink 事件。
  • 参数
    • udevudev 上下文。
    • name:监视器的名称,通常为 "udev"
  • 返回值:成功时返回指向 udev_monitor 结构的指针,失败时返回 NULL
  • 示例
    udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
    if (!udev_monitor) {qCritical() << "无法创建 udev 监视器";return;
    }
    

3. udev_monitor_filter_add_match_subsystem_devtype()

  • 原型int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype);
  • 作用:为 udev 监视器添加一个过滤器,只监听特定子系统和设备类型的事件。
  • 参数
    • udev_monitorudev 监视器。
    • subsystem:要监听的子系统,例如 "usb"
    • devtype:要监听的设备类型,可以为 NULL 表示匹配所有设备类型。
  • 返回值:成功时返回 0,失败时返回负数。
  • 示例
    udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "usb", NULL);
    

4. udev_monitor_enable_receiving()

  • 原型int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor);
  • 作用:启用 udev 监视器,开始接收事件。
  • 参数
    • udev_monitorudev 监视器。
  • 返回值:成功时返回 0,失败时返回负数。
  • 示例
    udev_monitor_enable_receiving(udev_monitor);
    

5. udev_monitor_get_fd()

  • 原型int udev_monitor_get_fd(struct udev_monitor *udev_monitor);
  • 作用:获取 udev 监视器的文件描述符,用于监听事件。
  • 参数
    • udev_monitorudev 监视器。
  • 返回值:成功时返回文件描述符,失败时返回负数。
  • 示例
    int udev_fd = udev_monitor_get_fd(udev_monitor);
    

6. udev_monitor_receive_device()

  • 原型struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor);
  • 作用:从 udev 监视器中读取下一个设备事件。
  • 参数
    • udev_monitorudev 监视器。
  • 返回值:成功时返回指向 udev_device 结构的指针,失败时返回 NULL
  • 示例
    struct udev_device *dev = udev_monitor_receive_device(udev_monitor);
    

7. udev_device_get_action()

  • 原型const char *udev_device_get_action(struct udev_device *udev_device);
  • 作用:获取设备事件的动作(例如 "add""remove")。
  • 参数
    • udev_device:设备对象。
  • 返回值:成功时返回动作字符串,失败时返回 NULL
  • 示例
    const char *action = udev_device_get_action(dev);
    

8. udev_device_get_devnode()

  • 原型const char *udev_device_get_devnode(struct udev_device *udev_device);
  • 作用:获取设备的设备节点路径(例如 /dev/sdb1)。
  • 参数
    • udev_device:设备对象。
  • 返回值:成功时返回设备节点路径字符串,失败时返回 NULL
  • 示例
    const char *devnode = udev_device_get_devnode(dev);
    

9. udev_device_get_devtype()

  • 原型const char *udev_device_get_devtype(struct udev_device *udev_device);
  • 作用:获取设备的类型(例如 "disk""partition")。
  • 参数
    • udev_device:设备对象。
  • 返回值:成功时返回设备类型字符串,失败时返回 NULL
  • 示例
    const char *devtype = udev_device_get_devtype(dev);
    

10. udev_device_unref()

  • 原型void udev_device_unref(struct udev_device *udev_device);
  • 作用:释放 udev_device 对象,减少其引用计数。
  • 参数
    • udev_device:设备对象。
  • 示例
    udev_device_unref(dev);
    

11. udev_monitor_unref()

  • 原型void udev_monitor_unref(struct udev_monitor *udev_monitor);
  • 作用:释放 udev_monitor 对象,减少其引用计数。
  • 参数
    • udev_monitorudev 监视器。
  • 示例
    udev_monitor_unref(udev_monitor);
    

12. udev_unref()

  • 原型void udev_unref(struct udev *udev);
  • 作用:释放 udev 上下文对象,减少其引用计数。
  • 参数
    • udevudev 上下文。
  • 示例
    udev_unref(udev);
    

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

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

相关文章

MFC工控项目实例二十九主对话框调用子对话框设定参数值

在主对话框调用子对话框设定参数值&#xff0c;使用theApp变量实现。 子对话框各参数变量 CString m_strTypeName; CString m_strBrand; CString m_strRemark; double m_edit_min; double m_edit_max; double m_edit_time2; double …

《C++在金融领域的技术革命:高效、安全与创新的融合》

引言 在当今数字化高速发展的时代&#xff0c;金融领域对技术的需求日益增长。C作为一种强大的编程语言&#xff0c;以其高效的执行性能、强大的内存管理能力和丰富的功能库&#xff0c;在金融领域中占据着重要的地位。本文将深入探讨 C在金融领域的热点技术应用&#xff0c;以…

mac上使用docker搭建gitlab

在 Mac 上搭建 GitLab 可以使用 Docker 来简化安装过程 一、安装详细步骤 1. 安装 Docker 如果你尚未安装 Docker&#xff0c;可以通过以下步骤安装&#xff1a; 下载并安装 Docker Desktop for Mac.安装完成后&#xff0c;启动 Docker Desktop&#xff0c;确保 Docker 运行…

x-cmd pkg | lf - 轻量级终端文件管理器,开销低,效率高,适合资源受限的环境

目录 简介快速上手安装使用 技术特点竞品和相关项目进一步阅读 简介 lf&#xff08;list files&#xff09;是 github.com/gokcehan 用 Go 开发的轻量级终端文件管理器&#xff0c;能提供一个通过键盘快捷键进行文件浏览、操作和管理的方法。它的界面风格和操作模式类似于 ran…

D64【python 接口自动化学习】- python基础之数据库

day64 SQL-DQL-基础查询 学习日期&#xff1a;20241110 学习目标&#xff1a;MySQL数据库-- 133 SQL-DQL-基础查询 学习笔记&#xff1a; 基础数据查询 基础数据查询-过滤 总结 基础查询的语法&#xff1a;select 字段列表|* from 表过滤查询的语法&#xff1a;select 字段…

力士乐工控机触摸屏面板维修CFG-BTV40.BN

力士乐工控机触摸屏维修包括BTV20系列&#xff0c;BTV30系列&#xff0c;BTV40等系列均可提供维修服务。 力士乐工控机维修&#xff0c;先区分故障是来自小信号处理部分&#xff0c;还是功率部分故障&#xff0c;很多设备的上位机会有所提示。处理部分包括i/o端口&#xff0c;…

数据挖掘全景:从基础理论到经典算法的深度探索

1 绪论--1.1 数据挖掘的概念和任务 1. (单选题)目前数据分析与挖掘领域的现实情况描述不正确的是&#xff08;&#xff09; A. 信息爆炸 B. 数据爆炸 C. 信息贫瘠 D.数据收集能力远远超过人们的分析和理解能力 2. (单选题)你认为下面哪种数据对于数据挖掘算法来说最简单最…

简易入手《SOM神经网络》的本质与原理

原创文章&#xff0c;转载请说明来自《老饼讲解神经网络》:www.bbbdata.com 关于《老饼讲解神经网络》&#xff1a; 本网结构化讲解神经网络的知识&#xff0c;原理和代码。 重现matlab神经网络工具箱的算法&#xff0c;是学习神经网络的好助手。 目录 一、入门原理解说 01.…

ubuntu 安装kafka-eagle

上传压缩包 kafka-eagle-bin-2.0.8.tar.gz 到集群 /root/efak 目录 cd /root/efak tar -zxvf kafka-eagle-bin-2.0.8.tar.gz cd /root/efak/kafka-eagle-bin-2.0.8 mkdir /root/efakmodule tar -zxvf efak-web-2.0.8-bin.tar.gz -C /root/efakmodule/ mv /root/efakmodule/efak…

小程序服务商常见问题

1: 服务器域名和开发域名都不带https前缀, 业务域名每个都需要校验文件 2: 手机开了调试可以请求, 关闭调试无法请求, 体验版接口请求不同 答: 服务商还需要通过接口给小程序设置业务域名; 但不需要校验文件; 注意: 体验版通过快速配置小程序服务器域名接口会不生效, 用普通的 …

Llama架构及代码详解

Llama的框架图如图&#xff1a; 源码中含有大量分布式训练相关的代码&#xff0c;读起来比较晦涩难懂&#xff0c;所以我们对llama自顶向下进行了解析及复现&#xff0c;我们对其划分成三层&#xff0c;分别是顶层、中层、和底层&#xff0c;如下&#xff1a; Llama的整体组成…

Docker在微服务架构中的最佳实践

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 Docker在微服务架构中的最佳实践 Docker在微服务架构中的最佳实践 Docker在微服务架构中的最佳实践 引言 Docker 概述 定义与原理…

排序算法 - 冒泡

文章目录 1. 冒泡排序1.1 简介1.2 基本步骤&#xff1a;1.3 示例代码&#xff08;C&#xff09;1.4 复杂度分析1.5 动画展示 1. 冒泡排序 1.1 简介 冒泡排序&#xff08;Bubble Sort&#xff09;是一种简单的排序算法&#xff0c;其基本思想是通过相邻元素的比较和交换&#…

Spring MVC 与 JSP 数据传输

在Spring MVC框架中&#xff0c;控制器&#xff08;Controller&#xff09;与视图&#xff08;View&#xff09;之间的数据传输是一个常见的操作。本文将介绍几种在Spring MVC中将数据从控制器传递到JSP页面的方法&#xff0c;并提供相应的代码案例。 1. 使用Model对象 Sprin…

理解 HTML5 Canvas 中逻辑像素与物理像素的关系

理解 HTML5 Canvas 中逻辑像素与物理像素的关系 在使用 HTML5 Canvas 时&#xff0c;开发者经常会遇到一个困惑&#xff1a;为什么鼠标的 offsetX 和 offsetY 和我绘制的图形坐标对不上&#xff1f;这通常是因为 Canvas 的逻辑像素大小和物理像素大小不一致。本文将详细解释这…

Nginx在Windows上和Linux上(Docker启动)分别配置基本身份认证示例

场景 Nginx代理的资源或网站等&#xff0c;url直接暴露有风险&#xff0c;需要添加身份认证&#xff0c;即输入用户名密码后才能成功访问。 注&#xff1a; 博客&#xff1a;霸道流氓气质-CSDN博客 实现 Windows上配置Nginx实现基本身份认证 修改nginx的配置文件 添加基…

丹摩征文活动|丹摩智算平台使用指南

目录 1. 登录平台与工作环境设置1.1 访问与登录1.2 创建或选择项目1.3 初始化项目环境 2. 数据上传与管理2.1 数据上传2.2 数据管理与预处理2.3 数据可视化 3. 模型构建与训练3.1 模型选择3.2 参数配置3.3 模型训练与评估 4. 模型部署与应用4.1 模型部署4.2 接口调用与集成4.3 …

用MVVM设计模式提升WPF开发体验:分层架构与绑定实例解析

MVVM&#xff08;Model-View-ViewModel&#xff09;是一种架构模式&#xff0c;广泛应用于现代前端开发&#xff0c;尤其是在微软的WPF&#xff08;Windows Presentation Foundation&#xff09;应用程序中。它旨在通过将视图&#xff08;UI&#xff09;与业务逻辑&#xff08;…

std::memory_order 多线程编程中的内存顺序

std::memory_order 是 C11 引入的一个枚举&#xff0c;用于控制多线程编程中的内存顺序。它定义了在原子操作中对内存的可见性和排序的要求。它在 C 标准库的 <atomic> 头文件中&#xff0c;配合原子类型和操作&#xff08;如 std::atomic 和 std::atomic_flag&#xff0…

MySql--多表查询及聚合函数总结

建议先阅读MySql--增删改查表设计总结-CSDN博客 一、聚合函数 1.COUNT(列||*)&#xff1a;统计结果的个数。 2.SUM&#xff08;列&#xff09;&#xff1a;求和。 3.AVG(列)&#xff1a;求平均值。 4.MIN(列) 最小值。 5.MAX(列) &#xff1a;最大值。 二、GROUP BY 分组查询…