golang查看CPU使用率與內(nèi)存的方法詳解
golang查看CPU使用率與內(nèi)存
1 psutil
1.1 概念與應(yīng)用場(chǎng)景
psutil是業(yè)內(nèi)一個(gè)用于監(jiān)控OS資源的軟件包,目前已經(jīng)多種語言,包括但不限于Python、Go。
- gopsutil是 Python 工具庫psutil 的 Golang 移植版,可以幫助我們方便地獲取各種系統(tǒng)和硬件信息。gopsutil為我們屏蔽了各個(gè)系統(tǒng)之間的差異,具有非常強(qiáng)悍的可移植性。有了gopsutil,我們不再需要針對(duì)不同的系統(tǒng)使用syscall調(diào)用對(duì)應(yīng)的系統(tǒng)方法。更棒的是gopsutil的實(shí)現(xiàn)中沒有任何cgo的代碼,使得交叉編譯成為可能。
應(yīng)用場(chǎng)景:go語言本身高并發(fā),無環(huán)境依賴部署,輕量級(jí),內(nèi)存占用低等特點(diǎn),加上gopsutil可以做性能監(jiān)控,系統(tǒng)監(jiān)控或系統(tǒng)信息采集,信息上報(bào)等
使用:
go get "github.com/shirou/gopsutil"
1.2 子包:CPU、disk、host、mem、net、process等
gopsutil將不同功能劃分到不同的子包中,使用時(shí)直接引入子包,然后調(diào)用,子包分類如下:
- CPU 相關(guān);
- disk 磁盤相關(guān)
- host 主機(jī)相關(guān)
- mem 內(nèi)存相關(guān)
- net 網(wǎng)絡(luò)相關(guān)
- process 進(jìn)程相關(guān)
- docke docker相關(guān)
①采集CPU
info方法包含cpu整個(gè)信息,Percent方法顯示cpu使用率,而load值需要引入load包
//打印cpu相關(guān)信息 info, _ := cpu.Info() for _, ci := range info { fmt.Println(ci) } //打印cpu使用率,每5秒一次,總共9次 for i := 1; i < 10; i++ { time.Sleep(time.Millisecond * 5000) percent, _ := cpu.Percent(time.Second, false) fmt.Printf("%v, cpu percent: %v", i, percent) } //顯示cpu load值 avg, _ := load.Avg() fmt.Println(avg)
②采集內(nèi)存
VirtualMemory方法顯示物理內(nèi)存信息,SwapMemory方法顯示交換內(nèi)存信息
//顯示物理內(nèi)存信息 memory, _ := mem.VirtualMemory() fmt.Printf("Total: %v, Free:%v,UsedPercent:%f%%\n",memory.Total, memory.Free, memory.UsedPercent) //顯示交換內(nèi)存信息 swapMemory, _ := mem.SwapMemory() fmt.Println(swapMemory)
import ( "github.com/shirou/gopsutil/v3/mem" ) func getMemInfo() { memInfo,err := mem.VirtualMemory() if err != nil { fmt.Println("get memory info fail. err: ", err) } // 獲取總內(nèi)存大小,單位GB memTotal := memInfo.Total/1024/1024/1024 // 獲取已用內(nèi)存大小,單位MB memUsed := memInfo.Used/1024/1024 // 可用內(nèi)存大小 memAva := memInfo.Available/1024/1024 // 內(nèi)存可用率 memUsedPercent := memInfo.UsedPercent fmt.Printf("總內(nèi)存: %v GB, 已用內(nèi)存: %v MB, 可用內(nèi)存: %v MB, 內(nèi)存使用率: %.3f %% \n",memTotal,memUsed,memAva,memUsedPercent) }
③采集主機(jī)信息
//顯示機(jī)器啟動(dòng)時(shí)間戳 bootTime, _ := host.BootTime() fmt.Println(bootTime) //顯示機(jī)器信息 info, _ := host.Info() fmt.Println(info) //顯示終端用戶 users, _ := host.Users() for _,user := range users { fmt.Println(user.User) }
④采集磁盤
Partitions方法顯示所有分區(qū)信息,Usage方法顯示分區(qū)使用量
//顯示磁盤分區(qū)信息 partitions, _ := disk.Partitions(true) for _,part := range partitions { fmt.Printf("part:%v\n",part.String()) usage, _ := disk.Usage(part.Mountpoint) fmt.Printf("disk info:used :%v free:%v\n",usage.UsedPercent,usage.Free) } //顯示磁盤分區(qū)IO信息 counters, _ := disk.IOCounters() for k,v := range counters { fmt.Printf("%v,%v\n",k,v) }
⑤采集進(jìn)程信息
//顯示所有進(jìn)程名稱和PID processes, _ := process.Processes() for _,process := range processes { fmt.Println(process.Pid) fmt.Println(process.Name()) }
⑥采集網(wǎng)絡(luò)信息
//為了避免和內(nèi)部包net沖突,改成net2 //顯示顯絡(luò)信息和IO counters, _ := net2.IOCounters(true) for k,v := range counters{ fmt.Printf("%v:%v send:%v recv:%v\n", k, v, v.BytesSent, v.BytesRecv) }
⑦采集docker信息
//顯示dockerID列表 list, _ := docker.GetDockerIDList() for _,v := range list{ fmt.Println(v) }
使用gopsutil工具包可以簡(jiǎn)單的就獲取系統(tǒng)相關(guān)信息,對(duì)于信息采集,系統(tǒng)監(jiān)控等場(chǎng)景非常方便就可以實(shí)現(xiàn)
對(duì)于復(fù)雜的進(jìn)程管理,process子包功能調(diào)用也比較復(fù)雜,這里推薦另一個(gè)操作系統(tǒng)使用systemctl管理進(jìn)程的包: github.com/coreos/go-systemd
1.3 實(shí)例
①獲取cpu、內(nèi)存、磁盤使用率
import github.com/shirou/gopsutil func GetCpuPercent() float64 { percent, _:= cpu.Percent(time.Second, false) return percent[0] } func GetMemPercent()float64 { memInfo, _ := mem.VirtualMemory() return memInfo.UsedPercent } func GetDiskPercent() float64 { parts, _ := disk.Partitions(true) diskInfo, _ := disk.Usage(parts[0].Mountpoint) return diskInfo.UsedPercent } func main() { fmt.Println(GetCpuPercent()) fmt.Println(GetMemPercent()) fmt.Println(GetDiskPercent()) }
結(jié)果:
7.8125
71
43.12042706933934
②獲取本機(jī)信息
info, _ := host.Info()fmt.Println(info)
結(jié)果:
{"hostname":"WIN-SP09TQCP1U8","uptime":25308,"bootTime":1558574107,"procs":175,"os":"windows","platform":"Microsoft Windows 10 Pro","platformFamily":"Standalone Workstation","platformVersion":"10.0.17134 Build 17134","kernelVersion":"","virtualizationSystem":"","virtualizationRole":"","hostid":。。。}
③獲取CPU信息
info, _ := cpu.Info() //總體信息 fmt.Println(info) //output: [{"cpu":0,cores":4,"modelName":"Intel(R) Core(TM) i5-2520M CPU @ 2.50GHz","mhz":2501,。。。] c, _ := cpu.Counts(true) //cpu邏輯數(shù)量 fmt.Println(c) //4 c, _ = cpu.Counts(false) //cpu物理核心 fmt.Println(c) //如果是2說明是雙核超線程, 如果是4則是4核非超線程
用戶CPU時(shí)間/系統(tǒng)CPU時(shí)間/空閑時(shí)間。。。等等
info, _ := cpu.Times(false) fmt.Println(info) //output: [{"cpu":"cpu-total","user":1272.0,"system":1572.7,"idle":23092.3,"nice":0.0,"iowait":0.0,"irq":0.0,。。。}] 用戶CPU時(shí)間:就是用戶的進(jìn)程獲得了CPU資源以后,在用戶態(tài)執(zhí)行的時(shí)間。 系統(tǒng)CPU時(shí)間:用戶進(jìn)程獲得了CPU資源以后,在內(nèi)核態(tài)的執(zhí)行時(shí)間。
CPU使用率,每秒刷新一次。
for{ info, _ := cpu.Percent(time.Duration(time.Second), false) fmt.Println(info) }
④獲取內(nèi)存(物理內(nèi)存和交換區(qū))信息
info, _ := mem.VirtualMemory() fmt.Println(info) info2, _ := mem.SwapMemory() fmt.Println(info2) //output: {"total":8129818624,"available":4193423360,"used":3936395264,"usedPercent":48,"free":0,"active":0,"inactive":0,...} {"total":8666689536,"used":4716843008,"free":3949846528,"usedPercent":0.5442496801583825,"sin":0,"sout":0,...}
總內(nèi)存大小是8129818624 = 8 GB,已用3936395264 = 3.9 GB,使用了48%。而交換區(qū)大小是8666689536 = 8 GB。
⑤獲取磁盤信息:分區(qū)、使用率、磁盤IO
info, _ := disk.Partitions(true) //所有分區(qū) fmt.Println(info) info2, _ := disk.Usage("E:") //指定某路徑的硬盤使用情況 fmt.Println(info2) info3, _ := disk.IOCounters() //所有硬盤的io信息 fmt.Println(info3) //output: [{"device":"C:","mountpoint":"C:","fstype":"NTFS","opts":"rw.compress"} {"device":"D:","mountpoint":"D:","fstype":"NTFS","opts":"rw.compress"} {"device":"E:","mountpoint":"E:","fstype":"NTFS","opts":"rw.compress"} ] {"path":"E:","fstype":"","total":107380965376,"free":46790828032,"used":60590137344,"usedPercent":56.425398236866755,"inodesTotal":0,"inodesUsed":0,"inodesFree":0,"inodesUsedPercent":0} map[C::{"readCount":0,"mergedReadCount":0,"writeCount":0,"mergedWriteCount":0,"readBytes":0,"writeBytes":4096,"readTime":0,"writeTime":0,"iopsInProgress":0,"ioTime":0,"weightedIO":0,"name":"C:","serialNumber":"","label":""} ...]
⑥獲取網(wǎng)絡(luò)信息
- 獲取當(dāng)前網(wǎng)絡(luò)連接信息
info, _ := net.Connections("all") //可填入tcp、udp、tcp4、udp4等等 fmt.Println(info) //output: [{"fd":0,"family":2,"type":1,"localaddr":{"ip":"0.0.0.0","port":135},"remoteaddr":{"ip":"0.0.0.0","port":0},"status":"LISTEN","uids":null,"pid":668} {"fd":0,"family":2,"type":1,"localaddr":{"ip":"0.0.0.0","port":445},"remoteaddr":{"ip":"0.0.0.0","port":0},"status":"LISTEN","uids":null,"pid":4} {"fd":0,"family":2,"type":1,"localaddr":{"ip":"0.0.0.0","port":1801},"remoteaddr":{"ip":"0.0.0.0","port":0},"status":"LISTEN","uids":null,"pid":3860} ...]
- 獲取網(wǎng)絡(luò)讀寫字節(jié)/包的個(gè)數(shù)
info, _ := net.IOCounters(false) fmt.Println(info) //output:[{"name":"all","bytesSent":6516450,"bytesRecv":36991210,"packetsSent":21767,"packetsRecv":33990,"errin":0,"errout":0,"dropin":0,"dropout":0,"fifoin":0,"fifoout":0}]
⑦獲取進(jìn)程信息
- 獲取到所有進(jìn)程信息
info, _ := process.Pids() //獲取當(dāng)前所有進(jìn)程的pid fmt.Println(info) //output: [0 4 96 464 636 740 748 816 852 880 976 348 564 668 912 1048 1120 1184 1268 1288。。。] info2,_ := process.GetWin32Proc(1120) //對(duì)應(yīng)pid的進(jìn)程信息 fmt.Println(info2) //output: [{svchost.exe 0xc00003e570 0xc00003e580 8 2019-05-23 09:15:28.444192 +0800 CST 5600 4 0xc00003e5b0 0 0 0 0 Win32_ComputerSystem WIN-SE89TTCP7U3 0xc00003e620 Win32_Process 0xc00003e650 0xc00005331e 209 6250000 0xc000053378 0xc0000533b8 Win32_OperatingSystem Microsoft Windows 10 專業(yè)版。。。}] fmt.Println(info2[0].ParentProcessID) //獲取父進(jìn)程的pid
2 golang獲取指定進(jìn)程的CPU利用率與內(nèi)存
2.1 代碼
golang獲取CPU利用率、內(nèi)存與資源管理器保持一致
- psutil返回的cpu percent是總的占用
- mac上的活動(dòng)監(jiān)視器展示的cpu使用率是沒有平均之后的
- 2. windows的資源管理器展示的是平均之后的,是占所有cpu核心的平均
package main import ( "fmt" "github.com/mitchellh/go-ps" "github.com/shirou/gopsutil/cpu" "github.com/shirou/gopsutil/process" "log" "runtime" "strings" "time" ) var ( s []string pid int ) func main() { processes, err2 := ps.Processes() if err2 != nil { log.Fatalf("%v", err2) } for _, p := range processes { //將___5go_build換為自己想要監(jiān)控的進(jìn)程名 if strings.Contains(p.Executable(), "___5go_build") { fmt.Println(p.Executable()) fmt.Println(p.Pid()) pid = p.Pid() } } p, err := process.NewProcess(int32(pid)) if err != nil { log.Fatal(err) } counts, err2 := cpu.Counts(true) if err2 != nil { log.Fatalf("%v\n", err2) } fmt.Println("counts=", counts) go func() { for { //cpuPercent, err := p.CPUPercent() //該方式不準(zhǔn)確 cpuPercent, err := p.Percent(time.Second * 3) //取樣3s內(nèi)的cpu使用, 返回的是總的cpu使用率;mac上不用除以cpuCounts if err != nil { log.Fatal(err) } memInfo, err := p.MemoryInfo() if err != nil { log.Fatal(err) } fmt.Printf("PID: %d\n", pid) switch runtime.GOOS { case "darwin": fmt.Printf("CPU占用: %.2f%%\n", cpuPercent) case "windows": fmt.Printf("CPU占用: %.2f%%\n", cpuPercent/float64(counts)) } //source used := processMemory.RSS fmt.Printf("內(nèi)存占用: %.2f MB\n", float64(memInfo.RSS)/1024/1024) //time.Sleep(time.Second * 1) } }() for { time.Sleep(time.Second * 5) fmt.Println("========== in process ==========") } }
2.2 效果
win10:
mac:
2.3 其他
1 通過go原生runtime包獲取進(jìn)程占用內(nèi)存
通過runtime.MemStats獲取Sys字段來展示進(jìn)程占用內(nèi)存大小
package main import ( "fmt" "runtime" "time" ) var ( count = 100000000 s []string ) func main() { for { if count > 0 { s = append(s, "431431242142142142") count-- if count%5000000 == 0 { collectMem() fmt.Println("count=", count) } } else { collectMem() } } } func collectMem() { var stat runtime.MemStats runtime.ReadMemStats(&stat) fmt.Printf("alloc:%vMB totalAlloc:%vMB sys:%vMB NumGC:%v\n", stat.Alloc/1024/1024, stat.TotalAlloc/1024/1024, stat.Sys/1024/1024, stat.NumGC) //fmt.Println("pid=", os.Getpid()) time.Sleep(time.Second * 5) } /* Alloc:已經(jīng)分配但還未被釋放的對(duì)象內(nèi)存總量,單位為字節(jié)(heap); TotalAlloc:運(yùn)行時(shí)系統(tǒng)已經(jīng)分配的內(nèi)存總量,單位為字節(jié); Sys:程序向操作系統(tǒng)申請(qǐng)的內(nèi)存總量,單位為字節(jié); NumGC:運(yùn)行時(shí)系統(tǒng)執(zhí)行的垃圾回收次數(shù); */
2 通過差值計(jì)算
如果是有用到rclone同步數(shù)據(jù),那么可以直接通過rclone獲取到的API來做差值:Sys - HeapRealeased
拓展:源碼里//go:指令
1. //go:linkname 符號(hào)別名
//go:linkname localname importpath.name
該指令指示編譯器使用 importpath.name 作為源代碼中聲明為 localname 的變量或函數(shù)的目標(biāo)文件符號(hào)名稱。但是由于這個(gè)偽指令,可以破壞類型系統(tǒng)和包模塊化,只有引用了 unsafe 包才可以使用。
簡(jiǎn)單來講,就是 importpath.name 是 localname 的符號(hào)別名,編譯器實(shí)際上會(huì)調(diào)用 localname。
使用的前提是使用了 unsafe 包才能使用。
案例:
import _ "unsafe" // for go:linkname //go:linkname time_now time.now func time_now() (sec int64, nsec int32, mono int64) { sec, nsec = walltime() return sec, nsec, nanotime() - startNano }
2. //go:noescape 禁用逃逸分析
該指令指定下一個(gè)有聲明但沒有主體(意味著實(shí)現(xiàn)有可能不是 Go)的函數(shù),不允許編譯器對(duì)其做逃逸分析。
一般情況下,該指令用于內(nèi)存分配優(yōu)化。編譯器默認(rèn)會(huì)進(jìn)行逃逸分析,會(huì)通過規(guī)則判定一個(gè)變量是分配到堆上還是棧上。
但凡事有意外,一些函數(shù)雖然逃逸分析其是存放到堆上。但是對(duì)于我們來說,它是特別的。我們就可以使用 go:noescape 指令強(qiáng)制要求編譯器將其分配到函數(shù)棧上。
案例:
// memmove copies n bytes from "from" to "to". // in memmove_*.s //go:noescape func memmove(to, from unsafe.Pointer, n uintptr)
我們觀察一下這個(gè)案例,它滿足了該指令的常見特性。如下:
- memmove_*.s:只有聲明,沒有主體。其主體是由底層匯編實(shí)現(xiàn)的
- memmove:函數(shù)功能,在棧上處理性能會(huì)更好
3. //go:nosplit 聲明該堆棧跳出溢出檢查
//go:nosplit
該指令指定文件中聲明的下一個(gè)函數(shù)不得包含堆棧溢出檢查。簡(jiǎn)單來講,就是這個(gè)函數(shù)跳過堆棧溢出的檢查。
案例:
//go:nosplit func key32(p *uintptr) *uint32 { return (*uint32)(unsafe.Pointer(p)) }
4. //go:nowritebarrierrec 處理讀寫屏障
//go:nowritebarrierrec
該指令表示編譯器遇到寫屏障時(shí)就會(huì)產(chǎn)生一個(gè)錯(cuò)誤,并且允許遞歸。也就是這個(gè)函數(shù)調(diào)用的其他函數(shù)如果有寫屏障也會(huì)報(bào)錯(cuò)。
簡(jiǎn)單來講,就是針對(duì)寫屏障的處理,防止其死循環(huán)。
案例:
//go:nowritebarrierrec func gcFlushBgCredit(scanWork int64) { ... }
5. //go:yeswritebarrierrec 與nowritebarrierrec相對(duì)
該指令與 go:nowritebarrierrec 相對(duì),在標(biāo)注 go:nowritebarrierrec 指令的函數(shù)上,遇到寫屏障會(huì)產(chǎn)生錯(cuò)誤。
而當(dāng)編譯器遇到 go:yeswritebarrierrec 指令時(shí)將會(huì)停止。
案例:
//go:yeswritebarrierrec func gchelper() { ... }
6. //go:noinline 禁止內(nèi)聯(lián)
//go:noinline func unexportedPanicForTesting(b []byte, i int) byte { return b[i] }
我們觀察一下這個(gè)案例,是直接通過索引取值,邏輯比較簡(jiǎn)單。如果不加上 go:noinline 的話,就會(huì)出現(xiàn)編譯器對(duì)其進(jìn)行內(nèi)聯(lián)優(yōu)化。
顯然,內(nèi)聯(lián)有好有壞。該指令就是提供這一特殊處理。
7. //go:norace 禁止靜態(tài)檢測(cè)
該指令表示禁止進(jìn)行競(jìng)態(tài)檢測(cè)。
常見的形式就是在啟動(dòng)時(shí)執(zhí)行 go run -race,能夠檢測(cè)應(yīng)用程序中是否存在雙向的數(shù)據(jù)競(jìng)爭(zhēng),非常有用。
//go:norace func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { ... }
8. //go:notinheap:不允許從堆申請(qǐng)內(nèi)存
該指令常用于類型聲明,它表示這個(gè)類型不允許從 GC 堆上進(jìn)行申請(qǐng)內(nèi)存。
在運(yùn)行時(shí)中常用其來做較低層次的內(nèi)部結(jié)構(gòu),避免調(diào)度器和內(nèi)存分配中的寫屏障,能夠提高性能。
案例:
// notInHeap is off-heap memory allocated by a lower-level allocator // like sysAlloc or persistentAlloc. // // In general, it's better to use real types marked as go:notinheap, // but this serves as a generic type for situations where that isn't // possible (like in the allocators). // //go:notinheap type notInHeap struct{}
以上就是golang查看CPU使用率與內(nèi)存的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于golang CPU使用率與內(nèi)存的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
golang實(shí)現(xiàn)圖像驗(yàn)證碼的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用golang實(shí)現(xiàn)簡(jiǎn)單的圖像驗(yàn)證碼,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-10-10Go垃圾回收提升內(nèi)存管理效率優(yōu)化最佳實(shí)踐
這篇文章主要為大家介紹了Go垃圾回收提升內(nèi)存管理效率優(yōu)化最佳實(shí)踐,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12詳解Go語言Sync.Pool為何不加鎖也能夠?qū)崿F(xiàn)線程安全
在這篇文章中,我們將剖析sync.Pool內(nèi)部實(shí)現(xiàn)中,介紹了sync.Pool比較巧妙的內(nèi)部設(shè)計(jì)思路以及其實(shí)現(xiàn)方式。在這個(gè)過程中,也間接介紹了為何不加鎖也能夠?qū)崿F(xiàn)線程安全,感興趣的可以學(xué)習(xí)一下2023-04-04golang如何實(shí)現(xiàn)三元運(yùn)算符功能
這篇文章主要介紹了在其他一些編程語言中,如?C?語言,三元運(yùn)算符是一種可以用一行代碼實(shí)現(xiàn)條件選擇的簡(jiǎn)便方法,那么在Go語言中如何實(shí)現(xiàn)類似功能呢,下面就跟隨小編一起學(xué)習(xí)一下吧2024-02-02