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

Linux下使用C++17获取指定USB端口连接设备的产品名称

Linux下C++17获取指定端口对应USB设备产品名实现方案

Linux内核会将所有USB设备的元信息暴露在sysfs虚拟文件系统中,无需解析dmesg输出、无需依赖第三方库,直接读取对应文件即可拿到设备产品名,稳定性远高于日志解析方案,且普通用户权限即可操作。

核心原理

所有挂载到用户空间的设备端口,在/sys/class/目录下都有对应条目:

  • ACM串口设备对应路径规则:/sys/class/tty/ttyACM<端口号>/device
  • HIDRaw设备对应路径规则:/sys/class/hidraw/hidraw<端口号>/device
    上述路径是指向USB设备接口目录的软链接,逐级向上遍历父目录,直到找到同时包含idVendor、product属性文件的目录,即为USB设备的根目录。其中product文件存储的纯文本内容,就是dmesg日志中显示的产品名称字段,同目录下还可读取manufacturer(厂商名)、serial(设备序列号)、idProduct(产品ID)等属性。

C++17 实现代码

使用C++17标准库的filesystem模块处理路径与软链接,无需额外依赖:

#include <filesystem>
#include <fstream>
#include <string>
#include <optional>
#include <algorithm>
#include <cctype>

namespace fs = std::filesystem;

std::optional<std::string> get_usb_product_name(const std::string& dev_path) {
    // 从/dev/xxx路径提取设备文件名
    std::string dev_name = fs::path(dev_path).filename().string();
    fs::path sys_dev_path;

    // 匹配不同设备类型的sysfs起始路径
    if (dev_name.size() >= 6 && dev_name.substr(0, 6) == "ttyACM") {
        sys_dev_path = fs::path("/sys/class/tty") / dev_name / "device";
    } else if (dev_name.size() >= 6 && dev_name.substr(0, 6) == "hidraw") {
        sys_dev_path = fs::path("/sys/class/hidraw") / dev_name / "device";
    } else {
        return std::nullopt; // 非支持的设备类型
    }

    if (!fs::exists(sys_dev_path)) {
        return std::nullopt; // 设备不存在
    }

    // 逐级向上查找USB设备根目录(包含product和idVendor文件的目录)
    fs::path current_path = fs::canonical(sys_dev_path);
    while (current_path != current_path.root_path()) {
        if (fs::exists(current_path / "idVendor") && fs::exists(current_path / "product")) {
            // 读取product文件内容
            std::ifstream product_file(current_path / "product");
            if (!product_file.is_open()) {
                return std::nullopt;
            }
            std::string product_name;
            std::getline(product_file, product_name);
            // 去除末尾可能的空白字符
            product_name.erase(std::find_if(product_name.rbegin(), product_name.rend(),
                [](unsigned char ch) { return !std::isspace(ch); }).base(), product_name.end());
            return product_name;
        }
        current_path = current_path.parent_path();
    }

    return std::nullopt;
}

// 调用示例
int main() {
    auto acm0_product = get_usb_product_name("/dev/ttyACM0");
    if (acm0_product.has_value()) {
        printf("ttyACM0 对应设备产品名: %s\n", acm0_product->c_str());
    }

    auto hidraw8_product = get_usb_product_name("/dev/hidraw8");
    if (hidraw8_product.has_value()) {
        printf("hidraw8 对应设备产品名: %s\n", hidraw8_product->c_str());
        // 示例场景下会输出: hidraw8 对应设备产品名: Teensyduino RawHID
    }
    return 0;
}

编译时需要开启C++17标准,GCC 9以下版本需额外链接filesystem库,编译命令参考:
g++ -std=c++17 usb_check.cpp -o usb_check -lstdc++fs

优化建议

  • 如果接入的是多台同型号设备,仅靠产品名无法区分,可读取同目录下的serial文件获取设备唯一序列号做永久绑定匹配,可靠性更高
  • 该方案默认支持ttyACM、hidraw两类设备,如果需要支持ttyUSB等其他USB转串口设备,只需要在路径匹配部分增加对应规则即可
  • 不推荐解析dmesg输出实现该功能:dmesg读取通常需要root权限,且系统日志轮转、历史日志残留都会导致解析结果错误

内容的提问来源于stack exchange,提问作者Marenator

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 15:15:32