请求协助:将已连接U盘的序列号或PID|VID映射至其挂载点
映射U盘PID/VID/序列号到树莓派挂载点的解决方案
我之前在树莓派上折腾过类似的需求——用libusb拿到U盘的PID/VID、序列号后,关联到它的实际挂载点(比如/media/pi/<flash_drive_name>)。这里分享几个我实际测试可行的方案,主要是通过系统设备信息+挂载文件来关联:
核心思路
U盘的挂载点记录在系统挂载文件中,但我们需要先把libusb拿到的PID/VID、序列号对应到U盘的块设备节点(比如/dev/sda1),再通过设备节点找到挂载点。
方案一:用libudev + 系统挂载文件(推荐,纯C实现)
libudev是Linux下管理设备的官方库,可以直接和libusb配合使用,不需要调用外部命令,稳定性更好。
步骤1:通过PID/VID/序列号找到块设备节点
先写一个函数遍历系统中的块设备,匹配我们从libusb拿到的参数:
#include <libudev.h> #include <stdio.h> #include <string.h> #include <stdlib.h> struct udev_device* find_target_usb_partition(struct udev *udev_ctx, const char *target_vid, const char *target_pid, const char *target_serial) { // 创建枚举器,筛选块设备中的分区 struct udev_enumerate *enumerator = udev_enumerate_new(udev_ctx); udev_enumerate_add_match_subsystem(enumerator, "block"); udev_enumerate_add_match_property(enumerator, "DEVTYPE", "partition"); udev_enumerate_scan_devices(enumerator); struct udev_list_entry *device_entries = udev_enumerate_get_list_entry(enumerator); struct udev_list_entry *entry; udev_list_entry_foreach(entry, device_entries) { const char *sys_path = udev_list_entry_get_name(entry); struct udev_device *dev = udev_device_new_from_syspath(udev_ctx, sys_path); // 提取设备的VID、PID、序列号属性 const char *dev_vid = udev_device_get_property_value(dev, "ID_VENDOR_ID"); const char *dev_pid = udev_device_get_property_value(dev, "ID_MODEL_ID"); const char *dev_serial = udev_device_get_property_value(dev, "ID_SERIAL_SHORT"); // 匹配目标参数 if (dev_vid && dev_pid && dev_serial && strcmp(dev_vid, target_vid) == 0 && strcmp(dev_pid, target_pid) == 0 && strcmp(dev_serial, target_serial) == 0) { udev_enumerate_unref(enumerator); return dev; // 返回匹配到的设备 } udev_device_unref(dev); } udev_enumerate_unref(enumerator); return NULL; // 未找到匹配设备 }
步骤2:通过设备节点获取挂载点
用getmntent函数读取/proc/mounts(系统实时挂载信息),匹配设备节点对应的挂载点:
#include <mntent.h> char* get_device_mount_point(const char *device_path) { FILE *mount_file = setmntent("/proc/mounts", "r"); if (!mount_file) return NULL; struct mntent *mount_entry; while ((mount_entry = getmntent(mount_file)) != NULL) { // 匹配设备路径 if (strcmp(mount_entry->mnt_fsname, device_path) == 0) { char *mount_point = strdup(mount_entry->mnt_dir); endmntent(mount_file); return mount_point; // 返回挂载点(需要后续free释放) } } endmntent(mount_file); return NULL; // 设备未挂载 }
步骤3:主程序调用示例
int main() { // 初始化udev上下文 struct udev *udev_ctx = udev_new(); if (!udev_ctx) { fprintf(stderr, "Failed to initialize udev context\n"); return 1; } // 替换成你从libusb拿到的实际参数 const char *target_vid = "XXXX"; // 注意是小写十六进制 const char *target_pid = "XXXX"; const char *target_serial = "XXXXXXXXXXXXX"; // 查找目标设备 struct udev_device *target_dev = find_target_usb_partition(udev_ctx, target_vid, target_pid, target_serial); if (target_dev) { const char *device_path = udev_device_get_devnode(target_dev); printf("Matched device node: %s\n", device_path); // 获取挂载点 char *mount_point = get_device_mount_point(device_path); if (mount_point) { printf("Mount point: %s\n", mount_point); free(mount_point); } else { printf("This device is not mounted yet\n"); } udev_device_unref(target_dev); } else { printf("No matching USB device found\n"); } udev_unref(udev_ctx); return 0; }
编译与运行
编译时需要链接libudev库:
gcc -o usb_mount_mapper usb_mapper.c -ludev
运行时可能需要root权限(访问udev信息):
sudo ./usb_mount_mapper
方案二:调用外部工具(快速验证)
如果只是快速验证或者不需要纯C实现,可以用命令行工具组合:
- 用
udevadm找到设备节点:
udevadm info --query=name --property --match-property=ID_VENDOR_ID=XXXX --match-property=ID_MODEL_ID=XXXX --match-property=ID_SERIAL_SHORT=XXXXXXXXXXXXX
- 用
findmnt找到挂载点:
findmnt -n -o TARGET /dev/sda1
在C程序中可以通过system()或者popen()调用这些命令,解析输出结果,但稳定性不如纯libudev实现。
注意事项
- 权限问题:普通用户可能无法访问udev的全部信息,建议程序运行时加
sudo,或者添加udev规则赋予普通用户权限。 - 多分区U盘:如果U盘有多个分区,上面的代码会匹配第一个符合条件的分区,需要根据需求调整筛选逻辑。
- 参数格式:libudev返回的VID/PID是小写十六进制字符串,要和libusb拿到的格式保持一致(注意大小写)。
内容的提问来源于stack exchange,提问作者MaxWolf
相关产品推荐
相关产品推荐

