登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go问答

Go 怎样读取子进程资源使用量

来源:17golang原创

时间:2026-10-14 23:57:56 373浏览 收藏

Go 可以在子进程结束并完成等待后,通过 cmd.ProcessState 读取 CPU 时间,再从 SysUsage() 获取系统相关的资源信息。Linux 下可将其断言为 *syscall.Rusage,读取 Maxrss 等字段。它适合一次任务结束后的统计,不是正在运行进程的实时内存采样接口。

假设我为批处理作业增加一条资源报告,我会把“执行成功了吗”和“消耗了多少资源”分开记录。失败作业也可能已消耗大量 CPU 或内存,提前因 Wait 返回错误就退出,会丢掉有价值的信息。下面是这种取舍的 Linux 示例,不是实际作业测量报告。

Go 官方文档:https://pkg.go.dev/os#ProcessState.SysUsage、https://pkg.go.dev/os/exec#Cmd.Wait;Linux 字段说明:https://man7.org/linux/man-pages/man2/getrusage.2.html。

我会先把统计对象限定为这一次启动的任务

这里的 cmd 是 Go 启动的一条命令,ProcessState 对应完成等待后得到的退出信息,Rusage 是其系统资源记录。要得到这一次命令的结果,应使用这个 Cmd 的 ProcessState,而不是查询父进程自身的累计资源量。

我会选结束后统计来给离线作业做归档,因为无需持续采样,也没有采样间隔的配置成本。但代价很明确:不能据此画实时曲线,也不能依靠结束后的 Maxrss 实时阻止内存继续增长。本文只讨论 Rusage 与 Wait,不实现资源限制器或进程树监控。

Cmd、子进程和 ProcessState 资源记录的归属关系
静态结构图解释一条任务与退出记录的对应关系,不是运行流程或实际监控截图。

普通执行权限就够,先保留真实启动错误

对子进程执行正常 Start 和 Wait,不需要为了读取该子进程退出后的统计而提升为 root。命令本身仍需要执行权限,工作目录、输入文件和环境也必须满足任务要求。如果文件不存在或不能执行,启动失败就没有这次成功运行后的资源报告,不能填一组零当作“任务未消耗资源”。

示例直接使用程序名称与参数列表,不经过 shell。对于来自不可信请求的命令,另行限制可执行程序与参数;资源统计不等于安全隔离。下面只适用于读者自己选择的可信命令。

Start 成功后,只让一个组件负责 Wait

将以下代码保存为 main_linux.go。构建约束故意限定 Linux,避免把系统特有字段与单位包装成所有系统通用接口。子进程输出交给标准错误流,资源摘要写入标准输出,避免把大段输出积存在内存缓冲区里。

//go:build linux

package main

import (
    "fmt"
    "os"
    "os/exec"
    "syscall"
    "time"
)

func run() int {
    if len(os.Args) 

Start 之后必须安排等待以回收进程相关资源,但不能让多个组件竞争同一子进程的等待状态。若使用 cmd.Run,它已经负责启动和等待,也应在 Run 返回后读取 ProcessState,不要再调用 Wait。这里选择拆开写,是为了明确区分启动失败与已启动任务的失败。

# 在空的 Linux 示例目录中初始化模块。
go mod init example.com/usage-report
# 构建资源记录程序。
go build -o usage-report .
# 使用常见的只读版本查询命令演示参数传递。
./usage-report go version

命令供读者执行,本文没有运行或提供虚构测量值。运行后应看到该命令的输出与摘要字段;具体 CPU 时间和内存量取决于环境,不能用固定数字作为验收依据。启动不存在的程序时应看到启动错误,不能声称得到完整 Rusage。

CPU 时间、墙钟耗时和峰值 RSS 不能互相替代

UserTime 与 SystemTime 分别提供用户态和内核态 CPU 时间,使用 Go 的 Duration 可避免手工转换 timeval 的秒与微秒。Go 文档描述的 CPU 口径包含已退出进程及其子进程;实际后代归集受到操作系统等待与资源记账规则影响,不能把它宣传为任意后台后代的完整监控。

wall 是示例在 Start 前到 Wait 返回的时间,包括启动、等待和运行经过的时间,不是纯 CPU 时间。多线程任务可以同时使用多个 CPU,累计 CPU 时间不必小于墙钟耗时;睡眠或等待 I/O 的任务则可能经历很长时间而消耗较少 CPU。

Linux 的 Maxrss 是峰值常驻集大小,单位 KiB。示例记录 max_rss_kib 和 max_rss_mib,不把它叫“当前内存”或“Go 堆内存”。不要把一次任务峰值当进程退出后仍然占用的内存,也不要将多个任务峰值简单相加当作同一时刻总量。

CPU 时间、墙钟耗时与峰值常驻内存的不同字段
静态说明图区分三类统计及其单位,不含虚构测量数值,也不是性能曲线。

失败任务有统计,不代表执行已成功

对已经运行并以非零状态退出的命令,Wait 可能返回错误,ProcessState 仍可用于记录资源。我的处理方式是先检查状态是否存在,再采集字段,最后返回失败,避免成功率报表和资源报表互相覆盖。

还要区分进程退出状态与 Wait 本身遇到的问题。state.Success 和 ExitCode 描述进程退出;waitErr 描述等待或执行相关错误。被信号终止时,ExitCode 可能为 -1,不要机械解释成普通业务退出码。需要进一步分析退出原因时再按系统类型读取退出信息,不和 SysUsage 的资源数据混用。

情况记录方式
Start 失败记录启动错误,不伪造资源量
已退出且状态可用记录退出状态及系统可用资源字段
Wait 返回非 nil 错误保留可用统计,但任务结果仍失败
资源类型不可用明确缺失,不用零值假装支持

累计子进程用量和跨平台单位要另写规则

能用 Getrusage(RUSAGE_CHILDREN) 替代吗?它提供调用进程已经等待回收的子进程累计口径,不是按指定 PID 查询这一次任务。父进程并发运行多个任务时,前后相减可能混入其他任务;Linux 文档还明确,其 Maxrss 是最大子进程的常驻集峰值,不是进程树总峰值。

能直接测整个 shell 流水线吗?示例统计自己启动并等待的命令,不承诺汇总任意管道或未被等待的后台后代。引入 shell 会改变被等待的对象,不能只看命令文本就确定统计覆盖面。

macOS 和 Windows 能照抄吗?不能。SysUsage 是系统相关返回值,Rusage 字段和单位应按目标平台分别确认。这里用 Linux 构建约束保留明确口径,不在其他系统直接输出同名 KiB 指标。

如果目标是离线作业归档,我会采用一次等待、一份退出状态、一组带平台和单位的资源字段;如果目标是实时监控或限制进程树用量,我会另选相应监控机制,而不扩大这份 Rusage 报告的能力。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>