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 …

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;其基本思想是通过相邻元素的比较和交换&#…

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

【数据库实验一】数据库及数据库中表的建立实验

目录 实验1 学习RDBMS的使用和创建数据库 一、 实验目的 二、实验内容 三、实验环境 四、实验前准备 五、实验步骤 六、实验结果 七、评价分析及心得体会 实验2 定义表和数据库完整性 一、 实验目的 二、实验内容 三、实验环境 四、实验前准备 五、实验步骤 六…

前端 JS面向对象 原型 prototype

目录 一、问题引出 二、prototype原型对象 三、小结 四、constructor 五、__proto__对象原型 六、原型链 一、问题引出 由于JS的构造函数存在内存浪费问题&#xff1a; function Star(name,age){this.namenamethis.ageagethis.singfunction () {console.log("唱歌&…

NodeJS的安装 npm 配置和使用 Vue-cli安装 Vue项目介绍

一.前端工程化 前端工程化是使用软件工程的方法来单独解决前端的开发流程中模块化、组件化、规范化、自动化的问题,其主要目的为了提高效率和降低成本 1. NodeJS的安装 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环 境&#xff0c;可以使 JavaScript 运行在服务…

软件工程概论项目(二),node.js的配置,npm的使用与vue的安装

上一章我们配置了git仓库&#xff0c;这一章我们来配置项目需要用的一些其他的环境。 放一个思维导图在这里&#xff0c;可以参考一下&#xff0c;很不全面&#xff0c;没有参考价值,反正我先这样写吧。 参考了这个nodejs的配置&#xff0c;写的很好&#xff1a;https://blog.c…

什么是crm?3000字详细解析

在现代商业环境中&#xff0c;客户关系管理&#xff08;CRM&#xff09;已经成为企业驱动成功的关键工具。在复杂且竞争激烈的市场中&#xff0c;如何有效地管理客户关系、提升客户满意度&#xff0c;并增加客户忠诚度&#xff0c;越来越成为企业迫切关心的问题。而CRM系统&…

3.1_文件上传漏洞

文件上传漏洞 文件上传漏洞原理&#xff1a;未对用户提交的文件进行严格校验&#xff0c;就将恶意文件解析执行&#xff0c;导致用户可以提交恶意的文件进行攻击&#xff1b; 利用方式&#xff08;危害&#xff09;&#xff1a; 1&#xff09;. 上传 HTML/SVG 进行 XSS 攻击&…