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は随時お待ちしております。