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}  # 指定可执行文件的安装路径
)

当然,我可以详细解释 CMakeLists.txt 文件中的每一项以及它们在编译过程中的作用。

编译过程中的内部操作

  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.xdnf.cn/news/13027.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【C++练习】使用C++编写程序计算π的近似值

题目&#xff1a;使用C编写程序计算π的近似值 描述&#xff1a; 编写一个C程序&#xff0c;使用一个特定的数学公式来计算圆周率&#xff08;π&#xff09;的近似值。该程序定义了一个函数calculatePi()&#xff0c;该函数通过一个迭代算法和一个涉及反正切函数&#xff08;…

Hook小程序

下载&#xff1a; https://github.com/JaveleyQAQ/WeChatOpenDevTools-Python 配置&#xff1a; pip install -r requirements 实现&#xff1a; 开启小程序开发者模式&#xff0c;类似浏览器F12 效果&#xff1a; 使用&#xff1a; 退出微信&#xff0c;进入安装的目录…

如何在pycharm中 判断是否成功安装pytorch环境

1、在电脑开始端&#xff0c;找到 2、打开后 在base环境下 输入conda env list 目前我的环境中没有pytorch 学习视频&#xff1a;【Anaconda、Pytorch、Pycharm到底是什么关系?什么是环境?什么是包?】https://www.bilibili.com/video/BV1CN411s7Ue?vd_sourcefad0750b8c6…

AI陪伴走热,网易云信“融合通讯+AI”新方案发布!附场景App及源码

信息秒回、不会失联、724h 情感陪伴、随时提供情绪价值......在 AI 能力越来越强大的今天&#xff0c;我们开始有了“AI 助手”、“AI 搭子”&#xff0c;甚至开始谈起“AI 男友/女友”&#xff0c;AI 的角色早已超越了简单的生产力工具&#xff0c;它正深入到我们生活的方方面…

力扣 LeetCode 704. 二分查找(Day1:数组)

解题思路&#xff1a; 二分查找主要分为[ left , right ]左闭右闭和[ left , right )左闭右开两种 此处采取[ left , right ]左闭右闭写法 注意&#xff1a; 1. right的初始化取值 2. while中取等 3. right mid -1 ; class Solution {public int search(int[] nums, i…

java-AOP编程示例

SpringBoot工程&#xff0c;有不懂的留言or Kimi一下 LogAspect.java package com.xxx.javaaopdemo.Aspect;import com.xxx.javaaopdemo.LogAnnotation; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang…

Kafka入门:Java客户端库的使用

在现代的分布式系统中&#xff0c;消息队列扮演着至关重要的角色&#xff0c;而Apache Kafka以其高吞吐量、可扩展性和容错性而广受欢迎。本文将带你了解如何使用Kafka的Java客户端库来实现生产者&#xff08;Producer&#xff09;和消费者&#xff08;Consumer&#xff09;的基…

使用 npm 安装 Yarn

PS E:\WeChat Files\wxid_fipwhzebc1yh22\FileStorage\File\2024-11\spid-admin\spid-admin> yarn install yarn : 无法将“yarn”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写&#xff0c;如果包括路径&#xff0c;请确保路径正确&#xff0c;然后…

力扣617:合并二叉树

给你两棵二叉树&#xff1a; root1 和 root2 。 想象一下&#xff0c;当你将其中一棵覆盖到另一棵之上时&#xff0c;两棵树上的一些节点将会重叠&#xff08;而另一些不会&#xff09;。你需要将这两棵树合并成一棵新二叉树。合并的规则是&#xff1a;如果两个节点重叠&#…

谷歌浏览器支持的开发者工具详解

谷歌浏览器&#xff08;Google Chrome&#xff09;是全球最受欢迎的网页浏览器之一&#xff0c;它不仅提供了快速、安全的浏览体验&#xff0c;还为开发者提供了强大的开发者工具。本文将详细介绍如何使用谷歌浏览器的开发者工具&#xff0c;并解答一些常见问题。&#xff08;本…

HTB:OpenAdmin[WriteUP]

目录 连接至HTB服务器并启动靶机 使用nmap对靶机TCP端口进行开放扫描 继续使用nmap对靶机22、80端口进行脚本、服务扫描 使用Dirbuster对靶机网页路径进行递归扫描 ​编辑 尝试在searchsploit中搜索改WebAPP漏洞 横向移动(其实没有横) 启动Metasploit 特权提升 USER_…

IO作业5

设置双信号实现交替生产者线程和消费者线程 #include <myhead.h> int n0; pthread_mutex_t fastmutex;//定义互斥锁 pthread_cond_t cond;//定义条件变量 pthread_cond_t cond2; void *product() {int i;for(i0;i<10;i){n;printf("我生产了一台特斯拉n%d\n"…

Web3.0安全开发实践|BNB链安全开发,这10大实用tips一定要收藏

BNB Chain是Web3世界中最受欢迎的区块链之一&#xff0c;其费用合理、交易迅速以及项目生态系统丰富几大原因吸引了广大用户。与任何的区块链都一样&#xff0c;BNB Chain上的开发者在开发过程中首先考虑的应该是安全问题&#xff1a;因为任何资金的损失都会导致用户对协议及平…

QT开发笔记之小知识

QCoreApplication::aboutToQuit 主事件循环退出前发出的信号&#xff0c;是程序退出前等待QT线程退出回收资源的神器。 官方帮助文档 [signal] void QCoreApplication::aboutToQuit() 该信号在应用程序即将退出主事件循环时发出&#xff0c;例如&#xff1a;当事件循环级别降至…

插入排序(C语言)

直接插入排序的基本思想&#xff1a;把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插入完为止&#xff0c;得到一个新的有序序列 。 一、步骤 1.给定一个乱序的数组&#xff0c;如 从第一个元素开始排序&#xff0c;当只…

文心一言 VS 讯飞星火 VS chatgpt (389)-- 算法导论25.1 2题

二、为什么要求对于所有的 1 ⩽ i ⩽ n 1⩽i⩽n 1⩽i⩽n&#xff0c;有 w i i 0 w_{ii}0 wii​0 &#xff1f;如果要写代码&#xff0c;请用go语言。 文心一言&#xff1a; 在许多数学和计算应用中&#xff0c;要求矩阵 W W W 的对角线元素 w i i 0 w_{ii} 0 wii​0 是…

Java多线程详解⑦(全程干货!!!)内存可见性 || volatile || JMM || wait notify notifyAll

这里是Themberfue 在上一节中&#xff0c;我们讨论了死锁的概念&#xff0c;产生的场景 &#xff0c;产生的必要条件...... 内存可见性 我们先来看一段百度百科关于 "内存可见性" 的解释 "内存可见性" 就是造成线程安全问题的原因之一 如果是单纯地看…

安装双系统(linux操作系统(debian)安装)

参考博客&#xff1a;戴尔服务器安装Debian11过程_戴尔t130安装debian-CSDN博客 一.腾出一个50G以上的空间&#xff0c;准备装操作系统 1.底部搜索计算机管理&#xff0c;选择磁盘管理 本人已预留400GB磁盘空间安装ubuntu系统&#xff0c;若没有预留空间&#xff0c;则可以选…

心系天下三星W25:记录盛世影像 见证华彩时光

悠然岁月中&#xff0c;被定格的瞬间总是历久弥新。心系天下三星W25以传世经典之姿跃然于掌中&#xff0c;将精致外形与精湛工艺合二为一&#xff0c;彰显出持有者的高雅气质。同时&#xff0c;强悍的影像系统则使之成为光影艺术的记录者&#xff0c;无论是捕捉人生风华&#x…

修改msyql用户密码及更新mysql密码策略

查看mysql中初始的密码策略 SHOW VARIABLES LIKE validate_password% 2. 修改默认密码策略 -- 0 或者 LOW 只验证长度-- 1 或者 MEDIUM 验证长度、数字、大小写、特殊字符-- 2 或者 STRONG 验证长度、数字、大小写、特殊字符、字典文件set global validate_password.policy0;…