gopsutil
是 Python 工具庫psutil
的 Golang 移植版,能夠幫助咱們方便地獲取各類系統和硬件信息。gopsutil
爲咱們屏蔽了各個系統之間的差別,具備很是強悍的可移植性。有了gopsutil
,咱們再也不須要針對不一樣的系統使用syscall
調用對應的系統方法。更棒的是gopsutil
的實現中沒有任何cgo
的代碼,使得交叉編譯成爲可能。node
先安裝:git
$ go get github.com/shirou/gopsutil
因爲gopsutil
庫用到了golang.org/x/sys
,後者在牆外,若是有相似下面的報錯:github
cannot find package "golang.org/x/sys/windows"
可以使用下面的命令下載golang.org/x/sys
在 GitHub 上的鏡像:golang
$ git clone git@github.com:golang/sys.git $GOPATH/src/golang.org/x/sys
使用:docker
package main import ( "fmt" "github.com/shirou/gopsutil/mem" ) func main() { v, _ := mem.VirtualMemory() fmt.Printf("Total: %v, Available: %v, UsedPercent:%f%%\n", v.Total, v.Available, v.UsedPercent) fmt.Println(v) }
gopsutil
將不一樣的功能劃分到不一樣的子包中:json
cpu
:CPU 相關;disk
:磁盤相關;docker
:docker 相關;host
:主機相關;mem
:內存相關;net
:網絡相關;process
:進程相關;winservices
:Windows 服務相關。想要使用對應的功能,要導入對應的子包。例如,上面代碼中,咱們要獲取內存信息,導入的是mem
子包。mem.VirtualMemory()
方法返回內存信息結構mem.VirtualMemoryStat
,該結構有豐富的字段,咱們最常使用的無外乎Total
(總內存)、Available
(可用內存)、Used
(已使用內存)和UsedPercent
(內存使用百分比)。mem.VirtualMemoryStat
還實現了fmt.Stringer
接口,以 JSON 格式返回內存信息。語句fmt.Println(v)
會自動調用v.String()
,將返回信息輸出。程序輸出:windows
Total: 8526921728, Available: 3768975360, UsedPercent:55.000000% {"total":8526921728,"available":3768975360,"used":4757946368,"usedPercent":55,"free":0,"active":0,"inactive":0,"wired":0,"laundry":0,"buffers":0,"cached":0,"writeback":0,"dirty":0,"writebacktmp":0,"shared":0,"slab":0,"sreclaimable":0,"sunreclaim":0,"pagetables":0,"swapcached":0,"commitlimit":0,"committedas":0,"hightotal":0,"highfree":0,"lowtotal":0,"lowfree":0,"swaptotal":0,"swapfree":0,"mapped":0,"vmalloctotal":0,"vmallocused":0,"vmallocchunk":0,"hugepagestotal":0,"hugepagesfree":0,"hugepagesize":0}
單位爲字節,個人電腦內存 8GB,當前使用百分比爲 55%,可用內存 3768975360B(即 3.51GB)。微信
咱們知道 CPU 的核數有兩種,一種是物理核數,一種是邏輯核數。物理核數就是主板上實際有多少個 CPU,一個物理 CPU 上能夠有多個核心,這些核心被稱爲邏輯核。gopsutil
中 CPU 相關功能在cpu
子包中,cpu
子包提供了獲取物理和邏輯核數、CPU 使用率的接口:網絡
Counts(logical bool)
:傳入false
,返回物理核數,傳入true
,返回邏輯核數;Percent(interval time.Duration, percpu bool)
:表示獲取interval
時間間隔內的 CPU 使用率,percpu
爲false
時,獲取總的 CPU 使用率,percpu
爲true
時,分別獲取每一個 CPU 的使用率,返回一個[]float64
類型的值。例如:app
func main() { physicalCnt, _ := cpu.Counts(false) logicalCnt, _ := cpu.Counts(true) fmt.Printf("physical count:%d logical count:%d\n", physicalCnt, logicalCnt) totalPercent, _ := cpu.Percent(3*time.Second, false) perPercents, _ := cpu.Percent(3*time.Second, true) fmt.Printf("total percent:%v per percents:%v", totalPercent, perPercents) }
上面代碼獲取物理核數和邏輯核數,並獲取 3s 內的總 CPU 使用率和每一個 CPU 各自的使用率,程序輸出(注意每次運行輸出可能都不相同):
physical count:4 logical count:8 total percent:[30.729166666666668] per percents:[32.64248704663213 26.94300518134715 44.559585492227974 23.958333333333336 36.787564766839374 20.3125 38.54166666666667 28.125]
調用cpu.Info()
可獲取 CPU 的詳細信息,返回[]cpu.InfoStat
:
func main() { infos, _ := cpu.Info() for _, info := range infos { data, _ := json.MarshalIndent(info, "", " ") fmt.Print(string(data)) } }
爲了方便查看,我使用 JSON 輸出結果:
{ "cpu": 0, "vendorId": "GenuineIntel", "family": "198", "model": "", "stepping": 0, "physicalId": "BFEBFBFF000906E9", "coreId": "", "cores": 8, "modelName": "Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz", "mhz": 3601, "cacheSize": 0, "flags": [], "microcode": "" }
由結果能夠看出,CPU 是 Intel 的 i7-7700 系列,頻率 3.60GHz。上面是我在 Windows 上運行的返回結果,內部使用了github.com/StackExchange/wmi
庫。在 Linux 下每一個邏輯 CPU 都會返回一個InfoStat
結構。
調用cpu.Times(percpu bool)
能夠獲取從開機算起,總 CPU 和 每一個單獨的 CPU 時間佔用狀況。傳入percpu=false
返回總的,傳入percpu=true
返回單個的。每一個 CPU 時間佔用狀況是一個TimeStat
結構:
// src/github.com/shirou/gopsutil/cpu/cpu.go type TimesStat struct { CPU string `json:"cpu"` User float64 `json:"user"` System float64 `json:"system"` Idle float64 `json:"idle"` Nice float64 `json:"nice"` Iowait float64 `json:"iowait"` Irq float64 `json:"irq"` Softirq float64 `json:"softirq"` Steal float64 `json:"steal"` Guest float64 `json:"guest"` GuestNice float64 `json:"guestNice"` }
CPU
:CPU 標識,若是是總的,該字段爲cpu-total
,不然爲cpu0
、cpu1
...;User
:用戶時間佔用(用戶態);System
:系統時間佔用(內核態);Idle
:空閒時間;例如:
func main() { infos, _ := cpu.Times(true) for _, info := range infos { data, _ := json.MarshalIndent(info, "", " ") fmt.Print(string(data)) } }
爲了方便查看,我用 JSON 輸出結果,下面是其中一個輸出:
{ "cpu": "cpu0", "user": 674.46875, "system": 1184.984375, "idle": 7497.1875, "nice": 0, "iowait": 0, "irq": 75.578125, "softirq": 0, "steal": 0, "guest": 0, "guestNice": 0 }
子包disk
用於獲取磁盤信息。disk
可獲取 IO 統計、分區和使用率信息。下面依次介紹。
調用disk.IOCounters()
函數,返回的 IO 統計信息用map[string]IOCountersStat
類型表示。每一個分區一個結構,鍵爲分區名,值爲統計信息。這裏摘取統計結構的部分字段,主要有讀寫的次數、字節數和時間:
// src/github.com/shirou/gopsutil/disk/disk.go type IOCountersStat struct { ReadCount uint64 `json:"readCount"` MergedReadCount uint64 `json:"mergedReadCount"` WriteCount uint64 `json:"writeCount"` MergedWriteCount uint64 `json:"mergedWriteCount"` ReadBytes uint64 `json:"readBytes"` WriteBytes uint64 `json:"writeBytes"` ReadTime uint64 `json:"readTime"` WriteTime uint64 `json:"writeTime"` // ... }
例如:
func main() { mapStat, _ := disk.IOCounters() for name, stat := range mapStat { fmt.Println(name) data, _ := json.MarshalIndent(stat, "", " ") fmt.Println(string(data)) } }
輸出包括全部分區,我這裏只展現一個:
C: { "readCount": 184372, "mergedReadCount": 0, "writeCount": 42252, "mergedWriteCount": 0, "readBytes": 5205152768, "writeBytes": 701583872, "readTime": 333, "writeTime": 27, "iopsInProgress": 0, "ioTime": 0, "weightedIO": 0, "name": "C:", "serialNumber": "", "label": "" }
注意,disk.IOCounters()
可傳入可變數量的字符串參數用於標識分區,此參數在 Windows 上無效。
調用disk.PartitionStat(all bool)
函數,返回分區信息。若是all = false
,只返回實際的物理分區(包括硬盤、CD-ROM、USB),忽略其它的虛擬分區。若是all = true
則返回全部的分區。返回類型爲[]PartitionStat
,每一個分區對應一個PartitionStat
結構:
// src/github.com/shirou/gopsutil/disk/ type PartitionStat struct { Device string `json:"device"` Mountpoint string `json:"mountpoint"` Fstype string `json:"fstype"` Opts string `json:"opts"` }
Device
:分區標識,在 Windows 上即爲C:
這類格式;Mountpoint
:掛載點,即該分區的文件路徑起始位置;Fstype
:文件系統類型,Windows 經常使用的有 FAT、NTFS 等,Linux 有 ext、ext二、ext3等;Opts
:選項,與系統相關。例如:
func main() { infos, _ := disk.Partitions(false) for _, info := range infos { data, _ := json.MarshalIndent(info, "", " ") fmt.Println(string(data)) } }
個人 Windows 機器輸出(只展現第一個分區):
{ "device": "C:", "mountpoint": "C:", "fstype": "NTFS", "opts": "rw.compress" }
由上面的輸出可知,個人第一個分區爲C:
,文件系統類型爲NTFS
。
調用disk.Usage(path string)
便可得到路徑path
所在磁盤的使用狀況,返回一個UsageStat
結構:
// src/github.com/shirou/gopsutil/disk.go type UsageStat struct { Path string `json:"path"` Fstype string `json:"fstype"` Total uint64 `json:"total"` Free uint64 `json:"free"` Used uint64 `json:"used"` UsedPercent float64 `json:"usedPercent"` InodesTotal uint64 `json:"inodesTotal"` InodesUsed uint64 `json:"inodesUsed"` InodesFree uint64 `json:"inodesFree"` InodesUsedPercent float64 `json:"inodesUsedPercent"` }
Path
:路徑,傳入的參數;Fstype
:文件系統類型;Total
:該分區總容量;Free
:空閒容量;Used
:已使用的容量;UsedPercent
:使用百分比。例如:
func main() { info, _ := disk.Usage("D:/code/golang") data, _ := json.MarshalIndent(info, "", " ") fmt.Println(string(data)) }
因爲返回的是磁盤的使用狀況,因此路徑D:/code/golang
和D:
返回一樣的結果,只是結構中的Path
字段不一樣而已。程序輸出:
{ "path": "D:/code/golang", "fstype": "", "total": 475779821568, "free": 385225650176, "used": 90554171392, "usedPercent": 19.032789388496106, "inodesTotal": 0, "inodesUsed": 0, "inodesFree": 0, "inodesUsedPercent": 0 }
子包host
能夠獲取主機相關信息,如開機時間、內核版本號、平臺信息等等。
host.BootTime()
返回主機開機時間的時間戳:
func main() { timestamp, _ := host.BootTime() t := time.Unix(int64(timestamp), 0) fmt.Println(t.Local().Format("2006-01-02 15:04:05")) }
上面先獲取開機時間,而後經過time.Unix()
將其轉爲time.Time
類型,最後輸出2006-01-02 15:04:05
格式的時間:
2020-04-06 20:25:32
func main() { version, _ := host.KernelVersion() fmt.Println(version) platform, family, version, _ := host.PlatformInformation() fmt.Println("platform:", platform) fmt.Println("family:", family, fmt.Println("version:", version) }
在個人 Win10 上運行輸出:
10.0.18362 Build 18362 platform: Microsoft Windows 10 Pro family: Standalone Workstation version: 10.0.18362 Build 18362
host.Users()
返回終端鏈接上來的用戶信息,每一個用戶一個UserStat
結構:
// src/github.com/shirou/gopsutil/host/host.go type UserStat struct { User string `json:"user"` Terminal string `json:"terminal"` Host string `json:"host"` Started int `json:"started"` }
字段一目瞭然,看示例:
func main() { users, _ := host.Users() for _, user := range users { data, _ := json.MarshalIndent(user, "", " ") fmt.Println(string(data)) } }
在快速開始中,咱們演示瞭如何使用mem.VirtualMemory()
來獲取內存信息。該函數返回的只是物理內存信息。咱們還可使用mem.SwapMemory()
獲取交換內存的信息,信息存儲在結構SwapMemoryStat
中:
// src/github.com/shirou/gopsutil/mem/ type SwapMemoryStat struct { Total uint64 `json:"total"` Used uint64 `json:"used"` Free uint64 `json:"free"` UsedPercent float64 `json:"usedPercent"` Sin uint64 `json:"sin"` Sout uint64 `json:"sout"` PgIn uint64 `json:"pgin"` PgOut uint64 `json:"pgout"` PgFault uint64 `json:"pgfault"` }
字段含義很容易理解,PgIn/PgOut/PgFault
這三個字段咱們重點介紹一下。交換內存是以頁爲單位的,若是出現缺頁錯誤(page fault
),操做系統會將磁盤中的某些頁載入內存,同時會根據特定的機制淘汰一些內存中的頁。PgIn
表徵載入頁數,PgOut
淘汰頁數,PgFault
缺頁錯誤數。
例如:
func main() { swapMemory, _ := mem.SwapMemory() data, _ := json.MarshalIndent(swapMemory, "", " ") fmt.Println(string(data)) }
process
可用於獲取系統當前運行的進程信息,建立新進程,對進程進行一些操做等。
func main() { var rootProcess *process.Process processes, _ := process.Processes() for _, p := range processes { if p.Pid == 0 { rootProcess = p break } } fmt.Println(rootProcess) fmt.Println("children:") children, _ := rootProcess.Children() for _, p := range children { fmt.Println(p) } }
先調用process.Processes()
獲取當前系統中運行的全部進程,而後找到Pid
爲 0 的進程,即操做系統的第一個進程,最後調用Children()
返回其子進程。還有不少方法可獲取進程信息,感興趣可查看文檔瞭解~
winservices
子包能夠獲取 Windows 系統中的服務信息,內部使用了golang.org/x/sys
包。在winservices
中,一個服務對應一個Service
結構:
// src/github.com/shirou/gopsutil/winservices/winservices.go type Service struct { Name string Config mgr.Config Status ServiceStatus // contains filtered or unexported fields }
mgr.Config
爲包golang.org/x/sys
中的結構,該結構詳細記錄了服務類型、啓動類型(自動/手動)、二進制文件路徑等信息:
// src/golang.org/x/sys/windows/svc/mgr/config.go type Config struct { ServiceType uint32 StartType uint32 ErrorControl uint32 BinaryPathName string LoadOrderGroup string TagId uint32 Dependencies []string ServiceStartName string DisplayName string Password string Description string SidType uint32 DelayedAutoStart bool }
ServiceStatus
結構記錄了服務的狀態:
// src/github.com/shirou/gopsutil/winservices/winservices.go type ServiceStatus struct { State svc.State Accepts svc.Accepted Pid uint32 Win32ExitCode uint32 }
State
:爲服務狀態,有已中止、運行、暫停等;Accepts
:表示服務接收哪些操做,有暫停、繼續、會話切換等;Pid
:進程 ID;Win32ExitCode
:應用程序退出狀態碼。下面程序中,我將系統中全部服務的名稱、二進制文件路徑和狀態輸出到控制檯:
func main() { services, _ := winservices.ListServices() for _, service := range services { newservice, _ := winservices.NewService(service.Name) newservice.GetServiceDetail() fmt.Println("Name:", newservice.Name, "Binary Path:", newservice.Config.BinaryPathName, "State: ", newservice.Status.State) } }
注意,調用winservices.ListServices()
返回的Service
對象信息是不全的,咱們經過NewService()
以該服務名稱建立一個服務,而後調用GetServiceDetail()
方法獲取該服務的詳細信息。不能直接經過service.GetServiceDetail()
來調用,由於ListService()
返回的對象缺乏必要的系統資源句柄(爲了節約資源),調用GetServiceDetail()
方法會panic
!!!
因爲大部分函數都涉及到底層的系統調用,因此發生錯誤和超時是在所不免的。幾乎全部的接口都有兩個返回值,第二個做爲錯誤。在前面的例子中,咱們爲了簡化代碼都忽略了錯誤,在實際使用中,建議對錯誤進行處理。
另外,大部分接口都是一對,一個不帶context.Context
類型的參數,另外一個帶有該類型參數,用於作上下文控制。在內部調用發生錯誤或超時後能及時處理,避免長時間等待返回。實際上,不帶context.Context
參數的函數內部都是以context.Background()
爲參數調用帶有context.Context
的函數的:
// src/github.com/shirou/gopsutil/cpu_windows.go func Times(percpu bool) ([]TimesStat, error) { return TimesWithContext(context.Background(), percpu) } func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) { // ... }
gopsutil
庫方便了咱們獲取本機的信息,且很好地處理了各個系統間的兼容問題,提供了一致的接口。還有幾個子包例如net/docker
限於篇幅沒有介紹,感興趣的童鞋可自行探索。
你們若是發現好玩、好用的 Go 語言庫,歡迎到 Go 每日一庫 GitHub 上提交 issue😄
歡迎關注個人微信公衆號【GoUpUp】,共同窗習,一塊兒進步~