【问题标题】:How to get CPU usage如何获取 CPU 使用率
【发布时间】:2019-12-29 07:36:29
【问题描述】:

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

我怎样才能获得它?

【问题讨论】:

  • 您使用哪种语言和操作系统?
  • 我在 Linux 下使用 Go(来自 golang.org),但如果可能的话,我想使用一些可移植到其他 *nix 的东西。
  • 不完全是一个答案,但我尝试了多种方法(包括读取 proc/stat)文件,然后意识到该服务部署在 Kubernetes 上,我们已经从 pod 报告了使用情况.所以我选择了那个。

标签: linux go


【解决方案1】:

查看这个包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
}

【讨论】:

    【解决方案2】:

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

    您还可以解析top -n1 | grep -i cpu 的输出以获取cpu 使用率,但它在我的linux 机器上仅采样半秒,并且在重负载时它很远。当我将常规顶部与以下程序同步时,它似乎非常匹配:

    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

    【讨论】:

      【解决方案3】:

      获取 CPU 使用率的机制取决于操作系统,因为这些数字对不同操作系统内核的含义略有不同。

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

      具体来说,每个进程的/proc/&lt;pid&gt;/stat 文件包含相关的进程记帐信息。它记录在proc(5) 中。您对 utimestimecutimecstime 字段特别感兴趣(从第 14 个字段开始)。

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

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

      【讨论】:

        【解决方案4】:

        您可以使用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")
            }
        }
        

        【讨论】:

        • 从命令输出中解析字符串并不是一个很好的解决方案,因为命令可以改变,而且它对 cpu/mem/etc 也很重。
        • @EricAnderson 您是否注意到接受的答案(写在我的一年多之后)也一样?您是否选择只回答第一个问题来提供问题的解决方案?
        • 这是一个很好的答案,因为它确实可以在 Mac OS X 10.10 上使用,只需复制和粘贴(而接受的答案则不能)
        • OP 标记了 Linux - 而不是 MacOS。因此,虽然这是一个不错的多操作系统解决方案,但脱壳并不是真正的好方法。
        【解决方案5】:

        这是一个独立于操作系统的解决方案,使用Cgo 来利用C standard library 提供的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
        }
        

        【讨论】:

        • 您可以将time.Now().Sub(startTime) 简化为time.Since(startTime)
        • 旁注:clock() 仅给出当前进程的时间。
        【解决方案6】:

        我最近不得不从 Raspberry Pi(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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-09-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多