CPUやメモリなどの情報を取得するgopsutilのご紹介

Go Advent Calendar 2015 の16日目です。

Pythonには psutil という、CPUやメモリなどの情報を取得するライブラリがあります。 拙作 gopsutil はこのpsutilをgolangに移植しようと始まりました。

gopsutilは、以下の特徴があります。

  • Linux/Darwin/FreeBSD/Windowsで動作します

    • もちろん、対応状況はかなり違います

  • (ほぼ) pure golangで実装されています。そのため、クロスコンパイルが容易です

    • ほぼ、というのはdarwinのCPU利用率だけcgoを使っています。cgoを使わない場合は単にnot implementedが返ってきます。

  • psutilにない情報も取れます

    • docker(cgroup)の情報だったり、仮想化状況だったり、好き勝手に機能を追加しています

gopsutilは1年半以上前からこつこつと開発を続けており、おかげさまで今ではgithubのスターが800以上を超えました。

また、

といったソフトウェアからライブラリとして使用されています。

使い方

使い方はREADMEに書いてありますが、以下の通りです。 github.com/shirou/gopsutil/mem などをimportして、 パッケージに置いてあるメソッドを呼び出すだけです。

import (
    "fmt"

    "github.com/shirou/gopsutil/mem"
)

func main() {
    v, _ := mem.VirtualMemory()

 // structが返ってきます。
    fmt.Printf("Total: %v, Free:%v, UsedPercent:%f%%\n", v.Total, v.Free, v.UsedPercent)

 // PrintするとJSON形式の結果が返ってきます
    fmt.Println(v)
}

これを実行するとこんな感じになります。

Total: 3179569152, Free:284233728, UsedPercent:84.508194%

{"total":3179569152,"available":492572672,"used":2895335424,"usedPercent":84.50819439828305, (以下省略)}

structとして取得できるので、あとは好きな様にいじって下さい。あるいは、PrintしてあげればJSONとして扱えますよ、という感じです。

取得できる情報

結構いろいろな情報が取れるのですが、その一部を紹介します。

  • CPU

    • CPU使用率、CPUのハードウェア情報

  • memory

    • メモリ使用率、スワップ使用率

  • disk

    • パーティション情報、I/O、ディスク使用率、ディスクのシリアル番号

  • host

    • ホスト名、起動時刻、OSや仮想化方式、

    • ログインユーザー情報

  • load

    • Load1, 5, 15

  • Process

    • 個々のプロセスのPIDや状態、起動プロセス名、メモリやCPU使用率など

  • Docker

    • コンテナ内部のCPU使用率やメモリ使用率など

要望があれば既存APIを壊さない範囲であればどんどん増やしていこうかな、と思っています。

中身

gopsutilは非常に泥臭いことをたくさんやっています。まず、pure goでいく、という大原則を立てているため、cgoは使えません。また、Linux/BSD/Windowsでは方式が大きく異なります。

Linux

procファイルシステムなど、ファイルベース

FreeBSD/Darwin

sysctl

Windows

DLL及びWMI

これらは cpu_darwin.go などのようにファイル名で分けています。

Linux

基本的にテキストファイルベースなので結構楽ですね。

と思いきや、Linuxのバージョンによって取れる情報が違ったり、コンテナ内部では /sys が使えないのでパスを入れ替えられるようにする必要があるなど、細かな点が異なります。

また、ユーザー情報は /var/run/utmp でバイナリ(utmp構造体)で格納されていますので、ちゃんとparseしてあげる必要があります。このあたりは2015年6月のGoConで 公開 しました(発表はしてません)。

FreeBSD/Darwin

BSD系は sysctl コマンドで各種の情報が取得できます。 sysctl vm.stats.vm.v_page_size でページサイズが取れたりですね。

ただし、sysctlコマンドで取得できるのはテキスト形式の情報だけです。Proc構造体の情報などはコマンドからは叩けないので、 syscall.Syscall6 などを使って叩きます。 (余談ですが、godocで出てくるのはLinuxのコードだけですので、Linux以外を知りたい場合はソースコードを読む必要があります)

mib := []int32{CTLKern, KernProc, KernProcProc, 0}
miblen := uint64(len(mib))

// まずlengthを0にして叩き、必要となるバッファ量を得る
length := uint64(0)
_, _, err := syscall.Syscall6(
    syscall.SYS___SYSCTL,
    uintptr(unsafe.Pointer(&mib[0])),
    uintptr(miblen),
    0,
    uintptr(unsafe.Pointer(&length)),
    0,
    0)

// 必要な情報を得る
buf := make([]byte, length)
_, _, err = syscall.Syscall6(
    syscall.SYS___SYSCTL,
    uintptr(unsafe.Pointer(&mib[0])),
    uintptr(miblen),
    uintptr(unsafe.Pointer(&buf[0])),
    uintptr(unsafe.Pointer(&length)),
    0,
    0)

ただし、Darwinは sysctl で取れる情報はFreeBSDに比べてかなり少ないので諦めたところもあります。

Windows

DLLを呼び出して情報を取得しています。

procGetDiskFreeSpaceExW := modkernel32.NewProc("GetDiskFreeSpaceExW")

diskret, _, err := procGetDiskFreeSpaceExW.Call(
     uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))),
     uintptr(unsafe.Pointer(&lpFreeBytesAvailable)),
     uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)),
     uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes)))

という感じですね。ただし、さすがにこれはいろいろツライので、 github.com/StackExchange/wmi を使ってWMIを叩くようにしています。

type Win32_Processor struct {
    LoadPercentage            *uint16
    Family                    uint16
    Manufacturer              string
    Name                      string
    NumberOfLogicalProcessors uint32
    ProcessorId               *string
    Stepping                  *string
    MaxClockSpeed             uint32
}

func get() {
    var dst []Win32_Processor
    q := wmi.CreateQuery(&dst, "")
    err := wmi.Query(q, &dst)
    if err != nil {
        return ret, err
    }
    fmt.Println(dst)
}

性能

測ってはいませんが、外部コマンドを呼んだりなどを気軽にしているため、そんなに性能はでないはずです。ものすごい高頻度で実行するとホスト側に負荷がかかるでしょう。その点は使う側で適宜キャッシュするなどをして頂ければと思います。

まとめ

ホストのCPUやメモリなどの情報を取得する gopsutil の紹介をしました。

作り始めたのがgoを使い始めて間もないころであり、さらにいろいろなプラットフォームに対する知見は後から得たりしていたので、統一感がなかったりします。そのうちちゃんとしたいと思ってはいるのですが…

もしもgoでシステムの情報を得たいと思った場合には、gopsutilのことを思い出していただけるとありがたく思います。また、PRは随時お待ちしております。

Comments

comments powered by Disqus