news 2026/9/13 14:28:39

Loki 仓库中的 prometheus/procfs:从 /proc 与 /sys 读取系统指标的核心 Go 库解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Loki 仓库中的 prometheus/procfs:从 /proc 与 /sys 读取系统指标的核心 Go 库解析

Loki 仓库中的 prometheus/procfs:从 /proc 与 /sys 读取系统指标的核心 Go 库解析

【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki

导读

procfs 是 Prometheus 生态中一个重要的 Go 基础库,它的全部职责只有一个:从 Linux 内核暴露的伪文件系统/proc/sys中读取系统、内核与进程指标。在 Loki 仓库中,它以v0.22.0版本被间接依赖(go.mod中的// indirect标注)并随 vendor 目录完整固化,为运行时指标采集、容器环境探测等能力提供底层支撑。读完本文,你将掌握 procfs 的包组织方式、FS类型与挂载点初始化原理、进程与块设备指标读取的完整调用链,以及其测试夹具(ttar)的更新流程。

项目定位:这不是 Loki 的私有实现,而是 vendored 的公共基础库

procfs 以 Go 模块github.com/prometheus/procfs的形态存在于本仓库中。它在 go.mod 中被声明为v0.22.0 // indirect——也就是说,Loki 并不直接调用它,而是通过 dskit、weaveworks/common 等间接依赖链引入,用于采集系统运行指标(例如内存、CPU、网络统计),最终暴露为 Prometheus 格式的指标。由于 Loki 采用 vendor 模式构建,其完整源码被固化在 vendor/github.com/prometheus/procfs 目录下,同时 vendor/modules.txt 中记录了它及其两个内部子包(internal/fsinternal/parsers)的 vendored 路径。

因此,本文讨论的"procfs"是 Loki 构建链中的一个受信任的第三方依赖。理解它的用法,等价于理解 Loki 乃至整个 Prometheus 技术栈中"如何从内核伪文件系统拿数据"的标准答案。

设计骨架:按数据来源划分的三个层次

README 明确指出,procfs 库按"数据来自/proc/sys还是两者"来组织包结构。这与内核伪文件系统的分工一致:

  • /proc:进程相关的运行时信息(进程列表、CPU 统计、内存统计等),大多数信息由根procfs包提供;
  • /sys:设备与内核子系统的结构化信息(块设备、PCI 等);
  • 两者都需要:部分子包,例如blockdevice,需要同时挂载两个伪文件系统。

在 vendor/github.com/prometheus/procfs/fs.go 中可以看到根包的核心抽象:

// FS represents the pseudo-filesystem sys, which provides an interface to // kernel data structures. type FS struct { proc fs.FS isReal bool }

注意其中的isReal字段:它用于标记当前挂载点是否指向真实的 proc 文件系统。在 fs.go 的 NewFS 实现 中,库会先调用internal/fsNewFS校验目录可读,再通过isRealProc做真实性探测——这保证了在容器环境或测试环境下(例如以目录为根的"伪 /proc")不会误判数据来源。

快速上手:初始化挂载点并读取统计信息

README 给出的最小用法示例,是初始化/proc挂载点后读取 CPU 统计:

fs, err := procfs.NewFS("/proc") stats, err := fs.Stat()

如果希望使用默认挂载点,可以更简单地调用procfs.NewDefaultFS()。从源码看,DefaultMountPoint常量定义于 fs.go,实际值来自internal/fsDefaultProcMountPoint(即标准的/proc路径)。

fs.Stat()返回的Stat结构体以 stat.go 中的CPUStat为核心,其中每个字段对应/proc/stat中的一列:

type CPUStat struct { User float64 Nice float64 System float64 Idle float64 Iowait float64 IRQ float64 SoftIRQ float64 Steal float64 Guest float64 GuestNice float64 }

这些字段与/proc/statcpu行的各列(user、nice、system、idle、iowait、irq、softirq、steal、guest、guest_nice)一一对应,单位为"USER_HZ"(通常为百分之一秒)。Loki 这类常驻服务在计算 CPU 使用率指标时,正是通过对两次采样差值做归一化来得到百分比。

块设备与双伪文件系统:blockdevice 子包的用法

部分指标横跨/proc/sys,README 以块设备为例给出调用方式:

fs, err := blockdevice.NewFS("/proc", "/sys") stats, err := fs.ProcDiskstats()

之所以需要同时传入两个挂载点,是因为ProcDiskstats需要解析/proc/diskstats(各磁盘的 I/O 累计计数),而设备号与设备名的对应关系、分区信息等细节需要借助/sys下的block目录来补全。这种"以/proc取计数、以/sys取拓扑"的组合方式,是 procfs 内部常见的配套策略。

根包同样提供磁盘统计入口:fs.Diskstats()直接对应 [proc_diskstats 相关实现],并经由internal/parsers包完成行解析与数值转换。

进程信息读取:Procs 与 Proc 类型

除了系统级统计,procfs 还提供了完整的进程级 API。在 proc.go 中:

// Proc provides information about a running process. type Proc struct { // The process ID. PID int fs FS } // Procs represents a list of Proc structs. type Procs []Proc

Proc将进程 ID 与挂载点FS绑定,通过方法族读取该进程在/proc/<pid>/下的各类文件。当前 vendor 目录下与进程相关的读取器包括:

文件对应 /proc 路径指标内容
proc_status.go/proc/<pid>/status进程状态、内存(VmRSS、VmSize)、线程数等
proc_stat.go/proc/<pid>/statCPU 时间片、启动时间等
proc_io.go/proc/<pid>/io读写字节数与 I/O 次数
proc_limits.go/proc/<pid>/limits资源软硬限制(RLIMIT)
proc_fdinfo.go/proc/<pid>/fdinfo文件描述符详情
proc_cgroup.go/proc/<pid>/cgroupcgroup 归属,容器环境下尤其关键
proc_environ.go/proc/<pid>/environ进程环境变量
proc_maps.go/proc/<pid>/maps内存映射区段

典型使用模式是先通过fs.AllProcs()fs.Procs()枚举全部 PID,再对每个Proc调用上述方法。这也是node_exporter等采集器实现process_*系列指标的标准路径。

系统级指标:覆盖内核各子系统的读取器

除进程外,根包还提供大量系统级读取器,全部以"一个源文件对应一个 Go 文件/结构体"的方式组织,便于对照内核文档阅读:

  • 内存:meminfo.go 对应/proc/meminfoMeminfo结构体中的MemTotalMemAvailableBuffersCachedSwapTotal等字段均以*uint64指针类型暴露——指针语义用于表达"该字段在当前内核版本上不存在"的情况,读取时需判空。该结构体还包含Zswap/Zswapped等较新内核字段;
  • CPU 与调度stat.go对应/proc/stat,另有 schedstat.go 对应/proc/<pid>/schedstat、softirqs.go 对应/proc/softirqs、[interrupts 相关] 对应/proc/interrupts
  • 网络:net_dev.go(网卡流量)、net_tcp.go、net_udp.go、net_unix.go、netstat.go(/proc/net/netstat扩展统计)、net_sockstat.go(socket 内存占用)等;
  • 内核信息:loadavg.go(系统负载)、vm.go(/proc/vmstat)、swaps.go、mdstat.go(软件 RAID 状态)、mountinfo.go(挂载表)、zoneinfo.go(NUMA zone 详情)。

这些读取器普遍遵循同一实现模式:用bufio逐行扫描伪文件 → 交给internal/parsers中的解析函数 → 填充对应结构体。解析失败时统一返回包级错误ErrFileParse(定义于 proc.go),调用方可用errors.Is判断。

内部基础设施:internal/fs 与 internal/parsers

vendored 版本中包含两个内部包,是理解实现细节的关键:

  • vendor/github.com/prometheus/procfs/internal/fs 提供fs.FS类型与DefaultProcMountPointDefaultSysMountPoint两个默认挂载点常量。所有公共NewFS最终都委托到这里完成目录校验;
  • vendor/github.com/prometheus/procfs/internal/parsers 集中了数值解析、文件读取(readfile.gosysreadfile.go)与键值解析(valueparser.go)等通用逻辑。由于/proc/sys中的文件是文本伪文件,解析器需要在"容忍注释行/空行"与"严格校验格式"之间做取舍,这一层正是策略落点。

构建与测试:make test与 ttar 测试夹具

README 强调:procfs 是设计为嵌入其他应用的库,不产出独立二进制,因此构建总是作为宿主项目(如 Loki)的一部分发生。其自带测试则可通过make test运行。

测试体系的独特之处在于测试夹具以 ttar 归档形式存储:目录下的ttar文件打包了大量从真实/proc/sys采样而来的示例文件,测试运行时自动解包,从而在不依赖真实内核的情况下完整覆盖解析逻辑。更新夹具的标准流程(摘自 README):

rm -rf testdata/fixtures make test

修改解包后的testdata/fixtures目录内容,然后重新打包:

make update_fixtures

最后用git diff testdata/fixtures.ttar校验变更。这套"真实采样 + 归档固化"的夹具机制,保证了新增解析逻辑能拿到贴近生产的输入数据,是 procfs 长期保持解析鲁棒性的关键工程实践。

版本与适用前提

本仓库固化的是github.com/prometheus/procfs v0.22.0。README 开篇明确警告该库仍处于 work in progress 状态,API 可能以不兼容方式变动——这正是 Loki 采用 vendor 模式锁定版本的直接原因:上游依赖的构建可复现性优先于跟随上游最新 API。

适用前提需注意:procfs 面向 Linux 内核的/proc/sys设计,非 Linux 平台无法提供等价语义;容器场景下/proc可能被挂载为宿主或容器命名空间视角,读取结果取决于挂载配置(这也解释了isReal探测的存在意义)。若要在自己的 Go 工程中使用,只需go get github.com/prometheus/procfs并遵循本文的初始化与调用模式;在 Loki 仓库内则直接阅读 vendor/github.com/prometheus/procfs 下的实现即可。

【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 14:26:28

ESP32开发环境搭建:WSL2+ESP-IDF五层可信构建指南

1. 为什么ESP32环境搭建总卡在“第一步”&#xff1f;——不是工具链问题&#xff0c;是认知断层你是不是也经历过&#xff1a;下载完ESP-IDF&#xff0c;执行install.bat后满屏红色报错&#xff1b;VS Code里点编译&#xff0c;提示command idf.build not found&#xff1b;或…

作者头像 李华
网站建设 2026/9/13 14:22:24

Python contextlib上下文管理实战:从with到ExitStack

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 14:20:31

AI辅助JS逆向:猿人学第18题动态Cookie破解

说实话&#xff0c;第一次刷到猿人学第18题的时候&#xff0c;我压根没想着能在半天内把它啃下来。题名看着就俩关键字&#xff1a;AI、逆向。但等你真正把页面打开&#xff0c;看到那串每次刷新都会变、还带着一堆混淆逻辑的__jsl_clearance_sCookie 时&#xff0c;才会意识到…

作者头像 李华
网站建设 2026/9/13 14:19:44

Android Studio新闻APP源码解析与答辩实战指南

简介&#xff1a;这是一套基于Android Studio开发的新闻APP项目源码&#xff0c;主要面向计算机专业完成本科毕业设计的学生&#xff0c;以及需要项目实战练习的初学者。项目涵盖实时新闻、娱乐、搞笑、原创、科技、体育、乐活等经典分类&#xff0c;支持每日精选编辑与优质内容…

作者头像 李华