Linux编程 SD/TF热插拔事件侦测

UDEV介绍

udev 是 Linux 系统中的设备管理工具,负责管理用户空间中的设备节点。它是 Linux 内核设备管理子系统的一部分,主要用于动态创建和删除 dev 目录下的设备文件,同时提供设备事件的通知机制。

udev主要功能

  1. 动态设备节点管理
  • 当设备插入或移除时,udev 会自动创建或删除 dev 目录下的设备节点。
  • 例如,当插入 USB 设备时,会在 dev 下创建对应的设备文件(如 /dev/sda)。
  1. 设备属性管理
  • udev 可以通过规则文件(rules.d)为设备分配特定的名称、权限或符号链接。
  • 例如,可以为某个特定的 USB 设备分配固定的设备名称。
  1. 设备事件通知
  • udev 提供了事件通知机制,允许用户空间程序监听设备的插拔事件。
  • 例如,可以通过 libudev 库监听设备的 add(插入)或 remove(移除)事件。
  1. 设备属性查询
  • udev 可以查询设备的属性信息,如设备类型、厂商、序列号等。

udev的组成

  1. udevd 守护进程
  • udevd 是 udev 的核心守护进程,负责处理内核发送的设备事件,并根据规则文件执行相应的操作。
  1. 规则文件
  • 位于 rules.d 或 rules.d,用于定义设备的行为。
  • 规则文件可以指定设备的名称、权限、符号链接等。
  1. libudev 库
  • 提供了用户空间程序与 udev 交互的接口,用于监听设备事件或查询设备信息。

使用场景

  1. 设备热插拔管理
  • 自动检测设备插入或移除,并执行相应的操作(如挂载文件系统、启动服务等)。
  1. 设备命名规则
  • 为设备分配固定的名称或符号链接,便于识别和使用。
  1. 设备事件监听
  • 通过 libudev 或 udevadm 工具监听设备事件,适用于需要动态响应设备变化的应用程序。
  1. 嵌入式设备管理
  • 在嵌入式系统中,udev 常用于管理 SD 卡、USB 设备、网络接口等硬件资源。

安装libudev

libudev-dev 是 Linux 系统中用于开发与设备管理相关程序的开发库。它提供了与 udev(设备管理守护进程)交互的接口,主要用于监听和管理设备事件。

$ sudo apt-get install libudev-dev

检查libudev是否安装成功

$ udevadm monitor --udev
KERNEL[12345.678901] add /devices/pci0000:00/0000:00:14.0/usb1/1-1 (usb)
UDEV  [12345.678901] add /devices/pci0000:00/0000:00:14.0/usb1/1-1 (usb)

示例代码

#include <libudev.h>
#include <iostream>int main() {struct udev* udev = udev_new();if (!udev) {std::cerr << "Failed to create udev context" << std::endl;return 1;}struct udev_monitor* monitor = udev_monitor_new_from_netlink(udev, "udev");udev_monitor_filter_add_match_subsystem_devtype(monitor, "block", NULL);udev_monitor_enable_receiving(monitor);int fd = udev_monitor_get_fd(monitor);while (true) {fd_set fds;FD_ZERO(&fds);FD_SET(fd, &fds);if (select(fd + 1, &fds, NULL, NULL, NULL) > 0 && FD_ISSET(fd, &fds)) {struct udev_device* dev = udev_monitor_receive_device(monitor);if (dev) {const char* action = udev_device_get_action(dev);const char* devnode = udev_device_get_devnode(dev);if (action && devnode) {std::cout << "Action: " << action << ", Device: " << devnode << std::endl;}udev_device_unref(dev);}}}udev_monitor_unref(monitor);udev_unref(udev);return 0;
}

创建 udev 上下文

调用 udev_new() 创建一个 udev 上下文,用于与 udev 系统交互。如果创建失败,程序会输出错误信息并退出。

struct udev* udev = udev_new();
if (!udev) {std::cerr << "Failed to create udev context" << std::endl;return 1;
}

创建 udev_monitor 监视器

  1. udev_monitor_new_from_netlink:创建一个 udev_monitor,用于监听设备事件。
  2. udev_monitor_filter_add_match_subsystem_devtype:设置过滤器,只监听 block 子系统的设备事件(如 SD 卡、USB 存储设备)。
  3. udev_monitor_enable_receiving:启用监视器接收事件。
struct udev_monitor* monitor = udev_monitor_new_from_netlink(udev, "udev");
udev_monitor_filter_add_match_subsystem_devtype(monitor, "block", NULL);
udev_monitor_enable_receiving(monitor);

获取监视器的文件描述符

获取 udev_monitor 的文件描述符,用于通过 select 系统调用监听事件。

int fd = udev_monitor_get_fd(monitor);

监听设备事件

  1. 使用 select 系统调用监听 udev_monitor 的文件描述符,等待设备事件。
  2. 当有事件发生时,调用 udev_monitor_receive_device 获取设备信息。
  3. udev_device_get_action:获取事件类型(如 add 表示设备插入,remove 表示设备移除)。
  4. udev_device_get_devnode:获取设备节点路径(如 /dev/sda)。
  5. 输出事件类型和设备节点路径。
while (true) {fd_set fds;FD_ZERO(&fds);FD_SET(fd, &fds);if (select(fd + 1, &fds, NULL, NULL, NULL) > 0 && FD_ISSET(fd, &fds)) {struct udev_device* dev = udev_monitor_receive_device(monitor);if (dev) {const char* action = udev_device_get_action(dev);const char* devnode = udev_device_get_devnode(dev);if (action && devnode) {std::cout << "Action: " << action << ", Device: " << devnode << std::endl;}udev_device_unref(dev);}}
}

插入SD卡

Action: add, Device: /dev/mmcblk0

移除SD卡

Action: remove, Device: /dev/mmcblk0