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