proc_parser - Linux 文件解析工具¶
Linux 下的硬件信息大多藏在 /proc 和 /sys 伪文件系统里。这些文件格式固定但处理起来很繁琐,而且容易出错——比如字段分隔符可能是冒号加空格,也可能是制表符。proc_parser 提供了一套专门处理这些格式的工具,用 string_view 避免拷贝,而且不抛异常。
解析 cpuinfo¶
/proc/cpuinfo 是 CPU 信息的核心来源,每行格式是 key: value。parse_cpuinfo_field() 提取指定字段的值:
#include "base/linux/proc_parser.h"
std::string line = "model name\t: Intel(R) Core(TM) i7-9750H";
auto model = cf::parse_cpuinfo_field(line, "model name");
// model == "Intel(R) Core(TM) i7-9750H"
// 字段不存在时返回空视图
auto missing = cf::parse_cpuinfo_field(line, "vendor_id");
// missing.data() == nullptr
实际使用时通常是逐行读取:
std::ifstream cpuinfo("/proc/cpuinfo");
std::string line;
while (std::getline(cpuinfo, line)) {
auto model = cf::parse_cpuinfo_field(line, "model name");
if (!model.empty()) {
std::cout << "CPU: " << model << std::endl;
break; // 找到就行,不需要继续
}
auto vendor = cf::parse_cpuinfo_field(line, "vendor_id");
if (!vendor.empty()) {
std::cout << "厂商: " << vendor << std::endl;
}
}
字符串去空格¶
这些文件里经常有多余的空格,trim_whitespace() 系列函数可以清理:
std::string_view sv = " hello world ";
auto trimmed = cf::trim_whitespace(sv); // "hello world"
auto left_trimmed = cf::ltrim_whitespace(sv); // "hello world "
auto right_trimmed = cf::rtrim_whitespace(sv);// " hello world"
解析缓存大小¶
缓存大小用的是人类可读的格式——32K、1M、2G。parse_cache_size() 统一转换成 KB:
auto size1 = cf::parse_cache_size("32K"); // 32
auto size2 = cf::parse_cache_size("1M"); // 1024
auto size3 = cf::parse_cache_size("2G"); // 2097152
auto invalid = cf::parse_cache_size("xyz"); // std::nullopt
这在读取 /sys/devices/system/cpu/cpu0/cache/index*/size 时特别有用:
auto l1_size = cf::read_uint32_file("/sys/devices/system/cpu/cpu0/cache/index0/size");
auto size_kb = cf::parse_cache_size(l1_size);
if (size_kb) {
std::cout << "L1 缓存: " << *size_kb << " KB" << std::endl;
}
解析数字¶
parse_uint32() 和 parse_hex_uint32() 把字符串转成数字,失败时返回 std::nullopt 而不是抛异常:
auto value = cf::parse_uint32("4096"); // 4096
auto hex1 = cf::parse_hex_uint32("0x41"); // 65
auto hex2 = cf::parse_hex_uint32("FF"); // 255
auto invalid = cf::parse_uint32("abc"); // std::nullopt
十六进制解析在处理 ARM 的 CPU implementer 字段时很常见:
auto impl = cf::parse_cpuinfo_field(line, "CPU implementer");
if (auto impl_val = cf::parse_hex_uint32(impl)) {
auto vendor = cf::arm_implementer_to_vendor(*impl_val);
// impl_val = 0x41 -> vendor = "ARM"
}
读取文件¶
read_uint32_file() 直接读取单个数字值的文件,这在 /sys 下很常见——很多文件就只有一个数字:
auto max_freq = cf::read_uint32_file(
"/sys/devices/system/cpu/cpu0/cpufreq/max_freq"
);
if (max_freq.has_value()) {
std::cout << "最大频率: " << *max_freq << " kHz" << std::endl;
}
⚠️ 这个函数会执行文件 I/O,可能因为权限或文件不存在而失败。返回 std::nullopt 时可以判断是文件问题还是解析失败。
ARM 厂商映射¶
ARM 的 CPU implementer 是个十六进制 ID,需要映射到厂商名称:
auto vendor = cf::arm_implementer_to_vendor(0x41); // "ARM"
auto vendor2 = cf::arm_implementer_to_vendor(0x51); // "Qualcomm"
auto vendor3 = cf::arm_implementer_to_vendor(0x69); // "Intel"
auto unknown = cf::arm_implementer_to_vendor(0xFF); // "Unknown"
支持的厂商 ID 包括 ARM (0x41)、Broadcom (0x42)、Qualcomm (0x51)、NVIDIA (0x4E) 等。
生命周期注意¶
所有返回 string_view 的函数,结果都依赖于输入数据的生命周期。不要这样做:
std::string_view bad() {
std::string line = "model name: Intel";
return cf::parse_cpuinfo_field(line, "model name"); // 危险!line 会被销毁
}
std::string_view good(const std::string& line) {
return cf::parse_cpuinfo_field(line, "model name"); // OK,调用方保证 line 有效
}