Linux下如何检测存储设备的挂载与移除事件?
检测Linux存储设备挂载/移除事件的实现方案
用sd-device(systemd设备库)实现事件监听
sd-device是systemd官方推荐替代libudev的库,以下是具体实现步骤和代码:
1. 依赖准备
确保系统安装了对应开发包:Debian/Ubuntu系安装libsystemd-dev,RHEL/CentOS系安装systemd-devel;编译时需链接-lsystemd参数。
2. 核心实现思路
通过sd-device创建设备监控器,订阅block类存储设备的事件(添加/移除/状态变更),结合事件循环处理异步事件,同时读取设备属性获取挂载状态等关键信息。
3. C++代码示例
#include <systemd/sd-device.h> #include <systemd/sd-event.h> #include <iostream> #include <cstring> #include <stdlib.h> static int device_monitor_handler(sd_device *dev, void *userdata) { const char *action, *devtype, *sysname; int ret; // 获取事件动作类型(add/remove/change等) ret = sd_device_get_action(dev, &action); if (ret < 0) { std::cerr << "获取设备动作失败: " << strerror(-ret) << std::endl; return 0; } // 仅处理磁盘类block设备,过滤无关设备 ret = sd_device_get_devtype(dev, &devtype); if (ret < 0 || strcmp(devtype, "disk") != 0) { return 0; } // 获取设备系统名称(如sda、sdb) ret = sd_device_get_sysname(dev, &sysname); if (ret < 0) { std::cerr << "获取设备系统名称失败: " << strerror(-ret) << std::endl; return 0; } // 读取挂载点(仅处理add/change事件) const char *mount_point = nullptr; if (strcmp(action, "add") == 0 || strcmp(action, "change") == 0) { sd_device *child; // 遍历子设备查找带文件系统的分区,获取挂载点 SD_DEVICE_FOREACH_CHILD(dev, child) { const char *fs_type; if (sd_device_get_property_value(child, "ID_FS_TYPE", &fs_type) >= 0 && strlen(fs_type) > 0) { sd_device_get_property_value(child, "MOUNT_POINT", &mount_point); break; } } } // 输出事件信息 std::cout << "设备事件: " << action << " - " << sysname; if (mount_point) { std::cout << " | 挂载路径: " << mount_point; } std::cout << std::endl; // 在这里添加你的业务逻辑 if (strcmp(action, "add") == 0 && mount_point) { std::cout << "执行挂载后操作: " << sysname << " 挂载于 " << mount_point << std::endl; } else if (strcmp(action, "remove") == 0) { std::cout << "执行移除后操作: " << sysname << std::endl; } return 0; } int main() { sd_event *event = nullptr; sd_device_monitor *monitor = nullptr; int ret; // 创建默认事件循环 ret = sd_event_default(&event); if (ret < 0) { std::cerr << "创建事件循环失败: " << strerror(-ret) << std::endl; goto cleanup; } // 创建设备监控器 ret = sd_device_monitor_new(&monitor); if (ret < 0) { std::cerr << "创建设备监控器失败: " << strerror(-ret) << std::endl; goto cleanup; } // 添加过滤规则:仅监听block子系统的disk设备 ret = sd_device_monitor_filter_add_match_subsystem_devtype(monitor, "block", "disk"); if (ret < 0) { std::cerr << "添加过滤规则失败: " << strerror(-ret) << std::endl; goto cleanup; } // 启动监控器,绑定事件循环与处理函数 ret = sd_device_monitor_start(monitor, event, device_monitor_handler, nullptr); if (ret < 0) { std::cerr << "启动监控器失败: " << strerror(-ret) << std::endl; goto cleanup; } std::cout << "开始监控存储设备事件..." << std::endl; // 运行事件循环,持续监听 ret = sd_event_loop(event); if (ret < 0) { std::cerr << "事件循环运行失败: " << strerror(-ret) << std::endl; } cleanup: // 资源释放 sd_device_monitor_unref(monitor); sd_event_unref(event); return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS; }
4. 代码关键点说明
- 事件循环:使用
sd-event实现异步事件处理,这是systemd生态的标准事件循环机制,无需手动处理IO多路复用。 - 设备过滤:通过
sd_device_monitor_filter_add_match_subsystem_devtype精准筛选存储设备,避免接收无关事件。 - 挂载状态判断:通过遍历子设备读取
MOUNT_POINT属性,准确识别设备是否完成挂载。 - 动作处理:针对
add/remove/change事件分别处理,覆盖设备插入、移除、挂载状态变更等场景。
5. 编译与运行
编译命令:
g++ -o device_monitor device_monitor.cpp -lsystemd
运行需root权限(访问udev事件需要权限):
sudo ./device_monitor
为什么不推荐监控/dev目录?
监控/dev存在以下问题:
- 仅能感知设备节点的创建/删除,无法获取设备类型、挂载状态、硬件信息等关键元数据。
- 会收到大量无关设备事件(如虚拟设备、字符设备),需额外过滤,效率低下。
- 无法区分设备是物理移除还是临时节点删除,可靠性差。
内容的提问来源于stack exchange,提问作者Anirudh Kachroo
相关产品推荐
相关产品推荐

