You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 20:51:14