如何获取CPU使用率

43

我的Go程序需要知道所有系统和用户进程的当前CPU使用百分比。

我该如何获得这些信息?


1
你使用哪种编程语言和操作系统? - Erdinç Taşkın
1
我在Linux上使用Go(来自golang.org),但如果可能的话,我想使用一些在其他*nix系统上也可以移植的工具。 - Sebastián Grignoli
不是一个确切的答案,但我尝试了多种方法来实现它(包括读取proc/stat文件),然后意识到该服务已部署在Kubernetes上,并且我们已经从pod报告了使用情况。所以我选择了这个方法。 - 0xC0DED00D
6个回答

34

看看这个包 http://github.com/c9s/goprocinfo,goprocinfo包会为您完成解析工作。

stat, err := linuxproc.ReadStat("/proc/stat")
if err != nil {
    t.Fatal("stat read fail")
}

for _, s := range stat.CPUStats {
    // s.User
    // s.Nice
    // s.System
    // s.Idle
    // s.IOWait
}

28

我曾遇到类似的问题,但从未找到一个轻量级的实现。这里是我的解决方案的精简版本,可以回答你的具体问题。我像tylerl建议的那样对/proc/stat文件进行采样。您会注意到,在采样之间等待3秒以匹配top的输出,但我使用1或2秒也取得了良好的结果。我在go例程中循环运行类似的代码,然后在需要时从其他go例程中访问cpu使用情况。

您还可以解析top -n1 | grep -i cpu的输出来获取CPU使用情况,但它在我的Linux机器上只采样半秒,并且在高负载期间偏差很大。当我将其与如下程序同步时,常规的top似乎非常接近:

package main

import (
    "fmt"
    "io/ioutil"
    "strconv"
    "strings"
    "time"
)

func getCPUSample() (idle, total uint64) {
    contents, err := ioutil.ReadFile("/proc/stat")
    if err != nil {
        return
    }
    lines := strings.Split(string(contents), "\n")
    for _, line := range(lines) {
        fields := strings.Fields(line)
        if fields[0] == "cpu" {
            numFields := len(fields)
            for i := 1; i < numFields; i++ {
                val, err := strconv.ParseUint(fields[i], 10, 64)
                if err != nil {
                    fmt.Println("Error: ", i, fields[i], err)
                }
                total += val // tally up all the numbers to get total ticks
                if i == 4 {  // idle is the 5th field in the cpu line
                    idle = val
                }
            }
            return
        }
    }
    return
}

func main() {
    idle0, total0 := getCPUSample()
    time.Sleep(3 * time.Second)
    idle1, total1 := getCPUSample()

    idleTicks := float64(idle1 - idle0)
    totalTicks := float64(total1 - total0)
    cpuUsage := 100 * (totalTicks - idleTicks) / totalTicks

    fmt.Printf("CPU usage is %f%% [busy: %f, total: %f]\n", cpuUsage, totalTicks-idleTicks, totalTicks)
}

看起来我可以链接到我在Bitbucket上编写的完整实现; 如果不行,请随意删除这条消息。目前它只适用于Linux:systemstat.go


19

获取CPU使用率的机制是依赖于操作系统的,因为这些数字在不同的操作系统内核下意义略有不同。

在Linux上,您可以通过读取/proc/文件系统中的伪文件来查询内核以获取最新的统计信息。当您读取它们来反映当前机器状态时,它们会即时生成。

具体而言,每个进程的/proc/<pid>/stat文件包含相关进程的记账信息。它在proc(5)中有记录。您特别关注第14个字段开始的utimestimecutimecstime字段。

您可以很容易地计算出百分比:只需读取数值,等待一段时间间隔,然后再次读取它们。取差值,除以您等待的时间量,就得到了您的平均值。这正是top程序所做的(以及执行相同服务的所有其他程序)。请记住,如果您有多个CPU,则可以超过100%的CPU使用率。

如果您只想要一个系统范围的摘要,那么可以在/proc/stat中报告--使用相同的技术计算您的平均值,但只需读取一个文件。


16
您可以使用 os.exec 包来执行 ps 命令并获取结果。
下面是一个程序,发出 ps aux 命令,解析结果并打印Linux上所有进程的CPU使用率:
package main

import (
    "bytes"
    "log"
    "os/exec"
    "strconv"
    "strings"
)

type Process struct {
    pid int
    cpu float64
}

func main() {
    cmd := exec.Command("ps", "aux")
    var out bytes.Buffer
    cmd.Stdout = &out
    err := cmd.Run()
    if err != nil {
        log.Fatal(err)
    }
    processes := make([]*Process, 0)
    for {
        line, err := out.ReadString('\n')
        if err!=nil {
            break;
        }
        tokens := strings.Split(line, " ")
        ft := make([]string, 0)
        for _, t := range(tokens) {
            if t!="" && t!="\t" {
                ft = append(ft, t)
            }
        }
        log.Println(len(ft), ft)
        pid, err := strconv.Atoi(ft[1])
        if err!=nil {
            continue
        }
        cpu, err := strconv.ParseFloat(ft[2], 64)
        if err!=nil {
            log.Fatal(err)
        }
        processes = append(processes, &Process{pid, cpu})
    }
    for _, p := range(processes) {
        log.Println("Process ", p.pid, " takes ", p.cpu, " % of the CPU")
    }
}

2
从命令输出中进行字符串解析并不是一个好的解决方案,因为命令可能会发生变化,并且对CPU、内存等资源消耗较大。 - Eric Anderson
@EricAnderson 你有没有注意到被采纳的答案(在我的答案一年后写的)也是做同样的事情?你选择只回答第一个来提供问题的解决方案吗? - Denys Séguret
1
这是一个很好的答案,因为它可以在Mac OS X 10.10上通过复制粘贴方式工作(而被接受的答案却不能)。 - Max Malysh
OP标记了Linux而不是MacOS。因此,虽然这是一个不错的多操作系统解决方案,但外壳并不是真正的正确方式。 - colm.anseo

8

这里是一个与操作系统无关的解决方案,使用 Cgo 来利用 C标准库 提供的 clock() 函数:

//#include <time.h>
import "C"
import "time"

var startTime = time.Now()
var startTicks = C.clock()

func CpuUsagePercent() float64 {
    clockSeconds := float64(C.clock()-startTicks) / float64(C.CLOCKS_PER_SEC)
    realSeconds := time.Since(startTime).Seconds()
    return clockSeconds / realSeconds * 100
}

2
你可以将 time.Now().Sub(startTime) 简化为 time.Since(startTime) - Dave C
附注:clock()仅提供当前进程的时间。 - bohdan_trotsenko

2

最近我需要从树莓派(Raspbian操作系统)中获取CPU使用率的测量数据,我使用了github.com/c9s/goprocinfo以及这里提出的方法:

这个想法来自于htop源代码,它是通过两次测量(先前/当前)来计算CPU使用率的:

func calcSingleCoreUsage(curr, prev linuxproc.CPUStat) float32 {

  PrevIdle := prev.Idle + prev.IOWait
  Idle := curr.Idle + curr.IOWait

  PrevNonIdle := prev.User + prev.Nice + prev.System + prev.IRQ + prev.SoftIRQ + prev.Steal
  NonIdle := curr.User + curr.Nice + curr.System + curr.IRQ + curr.SoftIRQ + curr.Steal

  PrevTotal := PrevIdle + PrevNonIdle
  Total := Idle + NonIdle
  // fmt.Println(PrevIdle, Idle, PrevNonIdle, NonIdle, PrevTotal, Total)

  //  differentiate: actual value minus the previous one
  totald := Total - PrevTotal
  idled := Idle - PrevIdle

  CPU_Percentage := (float32(totald) - float32(idled)) / float32(totald)

  return CPU_Percentage
}

更多信息请查看https://github.com/tgogos/rpi_cpu_memory


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接