- 开发工具
【免费下载链接】jc
CLI tool and python library that converts the output of popular command-line tools, file-types, and common strings to JSON, YAML, or Dictionaries. This allows piping of output to tools like jq and simplifying automation scripts.
本文围绕 jc 项目中的/proc/mtrr文件解析器(jc.parsers.proc_mtrr)展开,完整覆盖其命令行与 Python 模块两种调用方式、输出 Schema、原始/处理后输出差异,并结合 jc/parsers/proc_mtrr.py 源码与 tests/test_proc_mtrr.py 测试用例,讲清它如何把内核导出的 MTRR(Memory Type Range Register)寄存器信息逐行拆分为可被 jq、Python 脚本直接消费的结构化数据。
什么是 MTRR 与 /proc/mtrr
MTRR 是 x86 架构 CPU 提供的内存类型寄存器,用于指定某段物理地址范围的缓存行为。Linux 内核通过虚拟文件系统文件/proc/mtrr导出当前系统中生效的 MTRR 条目。其原始输出是一个定宽、混用冒号与逗号的文本列表,每行描述一个寄存器,形如:
reg00: base=0x000000000 ( 0MB), size= 2048MB, count=1: write-back reg01: base=0x080000000 ( 2048MB), size= 1024MB, count=1: write-back ... reg08: base=0xe8000000 (3712MB), size= 32MB: write-combining, count=1 reg11: base=0xfb000000 (4016MB), size= 4kB: uncachable, count=1其中type字段(write-back、write-combining、uncachable)表示缓存策略;base与括号中的 MB 值给出起始物理地址;size给出覆盖范围;count表示该条目使用的 MTRR 寄存器数量。这类输出常见于内存性能调优、GPU/设备 BAR 映射排查等场景,但原文本中数字与单位混杂、行内分隔符不统一,直接交给脚本处理很麻烦——这正是 jc 这个解析器解决的问题:把它转成 JSON / YAML / Python 字典,方便| jq或自动化脚本进一步加工。
使用方式:三种 CLI 形式与 Python 模块形式
与 jc 的其它 Proc 解析器一致,proc_mtrr支持三种等价的命令行用法:
# 方式一:通过 proc 通用解析器(自动识别 /proc/mtrr) $ cat /proc/mtrr | jc --proc # 方式二:magic 语法,直接把 /proc 文件路径交给 jc $ jc /proc/mtrr # 方式三:直接指定具体解析器 $ cat /proc/mtrr | jc --proc-mtrr在 Python 中以模块方式调用时,两种 parser 名都可以使用:
import jc # 通过 proc 通用解析器(自动分发) result = jc.parse('proc', proc_mtrr_file) # 或直接指定 proc_mtrr 解析器 result = jc.parse('proc_mtrr', proc_mtrr_file)关于 magic 语法,jc/parsers/proc.py 的模块文档还补充了两个实用细节:所有jc选项必须写在/proc路径之前;该语法还支持“slurp”方式一次传入多个文件(例如jc /proc/*/stat),输出会包在数组里,并附带_file字段用于关联输入输出,--meta-out选项可额外列出/proc输入文件名。
输出 Schema 与字段说明
解析后的结果为字典列表,每个字典对应一行 MTRR 寄存器记录:
[ { "register": string, "type": string, "base": string, "base_mb": integer, "size": integer, "count": integer, "<key>": string # additional key/values are strings } ]各字段含义如下:
| 字段 | 类型 | 来源与说明 |
|---|---|---|
register | string | 每行首段,如reg00、reg08,标识 MTRR 寄存器编号 |
type | string | 行尾的缓存类型:write-back/write-combining/uncachable等 |
base | string | 十进制与括号 MB 值之前的十六进制起始地址,保留原始字符串(含前导 0),如"0x000000000" |
base_mb | integer | 括号内标注的起始地址(MB 为单位),如2048 |
size | integer | size=后的数值部分,单位(MB/kB)在处理后被剥离,见下文说明 |
count | integer | 该条目占用的 MTRR 寄存器数量,通常为1 |
<key> | string | Schema 中保留的扩展位:任何额外解析出的key=value对都会以字符串形式并入 |
parse()函数签名与参数说明(与文档一致):
def parse(data: str, raw: bool = False, quiet: bool = False) -> List[Dict]| 参数 | 类型 | 说明 |
|---|---|---|
data | string | 待解析的/proc/mtrr文本 |
raw | boolean | 为True时返回未做类型处理的原始结构化输出 |
quiet | boolean | 为True时抑制告警信息 |
返回值是一个List[Dict],即原始或已处理的结构化数据。
完整示例:从 12 个寄存器到 JSON
仓库测试夹具 tests/fixtures/linux-proc/mtrr 收录了一份覆盖三种缓存类型、12 个寄存器(reg00–reg11)的真实样例输入,处理后的完整输出见 tests/fixtures/linux-proc/mtrr.json。取其中若干条展示(处理后):
$ cat /proc/mtrr | jc --proc -p[ { "register": "reg00", "type": "write-back", "base": "0x000000000", "base_mb": 0, "size": 2048, "count": 1 }, { "register": "reg08", "type": "write-combining", "base": "0xe8000000", "base_mb": 3712, "size": 32, "count": 1 }, { "register": "reg11", "type": "uncachable", "base": "0xfb000000", "base_mb": 4016, "size": 4, "count": 1 } ]而原始(-r)模式下的对应输出为:
$ cat /proc/mtrr | jc --proc-mtrr -p -r[ { "register": "reg00", "type": "write-back", "base": "0x000000000", "base_mb": "0", "size": "2048MB", "count": "1" }, { "register": "reg08", "type": "write-combining", "base": "0xe8000000", "base_mb": "3712", "size": "32MB", "count": "1" } ]两种模式的差异值得注意:
- raw 模式:
base_mb、size、count全部是字符串,且size保留原始单位("2048MB"、"4kB"),便于与内核原文逐字核对; - 默认(处理后)模式:这三个字段被转为整数,
size的单位后缀被剥离("4kB"→4)。也就是说,处理后的size是“原文数值”而非统一 MB 值——对 reg11 这样的 kB 级条目,size: 4实际表示 4kB。在下游做字节换算时应结合 raw 输出判断原始单位,这是使用该解析器时最需要留意的一个语义细节。
源码走读:proc_mtrr.py 的解析实现
解析实现位于 jc/parsers/proc_mtrr.py,核心只有parse()(L124-L183)与_process()(L102-L121)两个函数。
1. 入口校验。parse()先调用jc.utils.compatibility(__name__, info.compatible, quiet)检查运行平台(该解析器声明compatible = ['linux'],见 L94),再通过jc.utils.input_type_check(data)与jc.utils.has_data(data)做输入类型和空数据检查;空输入直接返回空列表。
2. 行级拆分。对每一行执行re.split(r',|:', line),即以逗号或冒号作为分隔符(L151)。这一步能同时兼容两种行内排版:
reg00: base=0x000000000 ( 0MB), size= 2048MB, count=1: write-back reg08: base=0xe8000000 (3712MB), size= 32MB: write-combining, count=1第一行中type在count之后,第二行中count在type之后——字段顺序并不固定。
3. 字段识别策略。拆分后:
- 第一个片段是寄存器名(
reg00等); - 第二个片段形如
base=0x000000000 ( 0MB),用split(maxsplit=1)拆出base十六进制串与括号内 MB 值,后者再经replace('(', '').replace(')', '').replace('MB', '').strip()清洗为纯数字(L156-L160); - 其余片段中,包含
=的一律按key=value收入键值字典(即size=...、count=...);不含=的则被认定为缓存类型type(L162-L167)。
这个“含等号即键值、不含等号即类型”的判别规则,正是该解析器能同时处理上面两种字段顺序、并对未来新增key=value字段保持开放的原因——这与 Schema 中<key>扩展位的设计相呼应。
4._process()的整型化。处理后阶段对每个条目的size、count、base_mb三个键调用 jc/utils.py 中的convert_to_int()(L252-L279):该函数用正则[^0-9\-\.]剥掉所有非数字字符(保留-与.)后再做int()转换,因此"2048MB"→2048、"4kB"→4。这也解释了前文提到的“处理后size丢失单位”的行为。
proc 通用解析器如何自动分发到 proc_mtrr
当使用jc --proc或 magic 语法jc /proc/mtrr时,并不直接调用proc_mtrr,而是由 jc/parsers/proc.py 的签名识别机制完成分发。该文件在parse()中维护了一组特征正则与解析器名的映射表procmap,其中 MTRR 的识别签名为(L177):
mtrr_p = re.compile(r'^reg\d+: base=0x[0-9a-f]+ \(')即要求至少一行以reg<数字>: base=0x<十六进制> (开头,命中后映射到proc_mtrr模块(L236),并动态加载调用其parse()。若内容无法被任何签名匹配,则抛出ParseError('Proc file could not be identified.')。这也意味着jc.parse('proc', data)与jc.parse('proc_mtrr', data)在 mtrr 文本上得到相同结果。
测试验证
单测 tests/test_proc_mtrr.py 使用夹具对(tests/fixtures/linux-proc/mtrr 原文 / tests/fixtures/linux-proc/mtrr.json 期望值)做了两项断言:
test_proc_mtrr_nodata:空字符串输入应返回[];test_proc_mtrr:对 12 行样例输入解析后的字典列表应与夹具 JSON 完全相等。
这保证了从write-back到uncachable的各类行、以及两种字段顺序排列(如 reg08 行size在type之前)都能被正确解析为整型化的结构化结果。测试可通过仓库根目录的 runtests.sh 随全量测试一起运行,单跑该解析器对应用例即可验证其回归稳定性。
解析器元信息与适用前提
从 jc/parsers/proc_mtrr.py 的info类可确认以下元数据:
| 元信息 | 值 |
|---|---|
| version | 1.0 |
| description | /proc/mtrrfile parser |
| author | Kelly Brazil |
| compatible | linux |
| tags | file |
| hidden | True |
适用前提与限制:
- 平台限制:
compatible = ['linux'],且输入必须是/proc/mtrr风格文本(x86 系内核的 MTRR 导出),在其它系统上运行兼容性检查时会给出告警; - hidden 标记:从源码结构看,该解析器被标记为
hidden = True,属于 jc 中不常出现在常规列表中的 Proc 细分解析器。按照 jc/parsers/proc.py 文档的说明,具体 Proc 文件解析器清单可通过jc -hh或jc -a查看,其 Schema 亦可借助jc --help --proc-mtrr打印; - 输入来源:解析器只依赖文本内容而非文件路径,因此除直接读
/proc/mtrr外,把保存下来的/proc/mtrr快照文件喂给它同样有效(测试夹具正是这样做的); - 下游使用:默认整数化输出适合直接
| jq '.[] | select(.type=="write-combining")'这类过滤统计;若需要保留MB/kB单位原文做二次换算,应加-r使用 raw 输出。
小结
jc.parsers.proc_mtrr用不到 200 行代码(含文档字符串)解决了/proc/mtrr这类“分隔符混用、字段顺序不定”的内核导出文本的结构化问题:以“含等号即键值、不含等号即类型”的启发式实现了对多种行内排版的兼容,通过convert_to_int提供数值化默认输出,同时保留-r原始模式以便追溯原文单位。无论是编写内存类型排查脚本,还是把 MTRR 配置纳入监控采集,cat /proc/mtrr | jc --proc-mtrr都是把它变成可计算数据的最短路径。
- 开发工具
【免费下载链接】jc
CLI tool and python library that converts the output of popular command-line tools, file-types, and common strings to JSON, YAML, or Dictionaries. This allows piping of output to tools like jq and simplifying automation scripts.
相关推荐
jc proc_meminfo 解析器详解:把 Linux /proc/meminfo 内存信息文件转换为结构化 JSON
jc proc_meminfo 解析器详解:把 Linux /proc/meminfo 内存信息文件转换为结构化 JSON jc(JSON Convert)中的
开发工具jc proc_loadavg 解析器详解:把 /proc/loadavg 负载数据结构化输出为 JSON
jc proc_loadavg 解析器详解:把 /proc/loadavg 负载数据结构化输出为 JSON 本文围绕 jc 仓库中的 proc_loadavg
开发工具jc 的 /proc/locks 解析器:把 Linux 文件锁信息一键结构化为 JSON
jc 的 /proc/locks 解析器:把 Linux 文件锁信息一键结构化为 JSON jc 项目为 Linux 的 /proc/locks 伪文件系统提供
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考