mirror of
https://github.com/shirou/gopsutil.git
synced 2025-05-01 13:48:52 +08:00

manual available memory calculation should be done if only memavail is false Manual Available Memory calculation under kernel 3.14 accurate manaul available memory calculation
198 lines
4.4 KiB
Go
198 lines
4.4 KiB
Go
// +build linux
|
|
|
|
package mem
|
|
|
|
import (
|
|
"context"
|
|
"math"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/shirou/gopsutil/internal/common"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
func VirtualMemory() (*VirtualMemoryStat, error) {
|
|
return VirtualMemoryWithContext(context.Background())
|
|
}
|
|
|
|
func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) {
|
|
filename := common.HostProc("meminfo")
|
|
lines, _ := common.ReadLines(filename)
|
|
// flag if MemAvailable is in /proc/meminfo (kernel 3.14+)
|
|
memavail := false
|
|
|
|
ret := &VirtualMemoryStat{}
|
|
for _, line := range lines {
|
|
fields := strings.Split(line, ":")
|
|
if len(fields) != 2 {
|
|
continue
|
|
}
|
|
key := strings.TrimSpace(fields[0])
|
|
value := strings.TrimSpace(fields[1])
|
|
value = strings.Replace(value, " kB", "", -1)
|
|
|
|
t, err := strconv.ParseUint(value, 10, 64)
|
|
if err != nil {
|
|
return ret, err
|
|
}
|
|
switch key {
|
|
case "MemTotal":
|
|
ret.Total = t * 1024
|
|
case "MemFree":
|
|
ret.Free = t * 1024
|
|
case "MemAvailable":
|
|
memavail = true
|
|
ret.Available = t * 1024
|
|
case "Buffers":
|
|
ret.Buffers = t * 1024
|
|
case "Cached":
|
|
ret.Cached = t * 1024
|
|
case "Active":
|
|
ret.Active = t * 1024
|
|
case "Inactive":
|
|
ret.Inactive = t * 1024
|
|
case "Writeback":
|
|
ret.Writeback = t * 1024
|
|
case "WritebackTmp":
|
|
ret.WritebackTmp = t * 1024
|
|
case "Dirty":
|
|
ret.Dirty = t * 1024
|
|
case "Shmem":
|
|
ret.Shared = t * 1024
|
|
case "Slab":
|
|
ret.Slab = t * 1024
|
|
case "SReclaimable":
|
|
ret.SReclaimable = t * 1024
|
|
case "PageTables":
|
|
ret.PageTables = t * 1024
|
|
case "SwapCached":
|
|
ret.SwapCached = t * 1024
|
|
case "CommitLimit":
|
|
ret.CommitLimit = t * 1024
|
|
case "Committed_AS":
|
|
ret.CommittedAS = t * 1024
|
|
case "HighTotal":
|
|
ret.HighTotal = t * 1024
|
|
case "HighFree":
|
|
ret.HighFree = t * 1024
|
|
case "LowTotal":
|
|
ret.LowTotal = t * 1024
|
|
case "LowFree":
|
|
ret.LowFree = t * 1024
|
|
case "SwapTotal":
|
|
ret.SwapTotal = t * 1024
|
|
case "SwapFree":
|
|
ret.SwapFree = t * 1024
|
|
case "Mapped":
|
|
ret.Mapped = t * 1024
|
|
case "VmallocTotal":
|
|
ret.VMallocTotal = t * 1024
|
|
case "VmallocUsed":
|
|
ret.VMallocUsed = t * 1024
|
|
case "VmallocChunk":
|
|
ret.VMallocChunk = t * 1024
|
|
case "HugePages_Total":
|
|
ret.HugePagesTotal = t
|
|
case "HugePages_Free":
|
|
ret.HugePagesFree = t
|
|
case "Hugepagesize":
|
|
ret.HugePageSize = t * 1024
|
|
}
|
|
}
|
|
|
|
ret.Cached += ret.SReclaimable
|
|
|
|
if !memavail {
|
|
ret.Available = calcuateAvailVmem(ret)
|
|
}
|
|
|
|
ret.Used = ret.Total - ret.Free - ret.Buffers - ret.Cached
|
|
ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
func SwapMemory() (*SwapMemoryStat, error) {
|
|
return SwapMemoryWithContext(context.Background())
|
|
}
|
|
|
|
func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) {
|
|
sysinfo := &unix.Sysinfo_t{}
|
|
|
|
if err := unix.Sysinfo(sysinfo); err != nil {
|
|
return nil, err
|
|
}
|
|
ret := &SwapMemoryStat{
|
|
Total: uint64(sysinfo.Totalswap) * uint64(sysinfo.Unit),
|
|
Free: uint64(sysinfo.Freeswap) * uint64(sysinfo.Unit),
|
|
}
|
|
ret.Used = ret.Total - ret.Free
|
|
//check Infinity
|
|
if ret.Total != 0 {
|
|
ret.UsedPercent = float64(ret.Total-ret.Free) / float64(ret.Total) * 100.0
|
|
} else {
|
|
ret.UsedPercent = 0
|
|
}
|
|
filename := common.HostProc("vmstat")
|
|
lines, _ := common.ReadLines(filename)
|
|
for _, l := range lines {
|
|
fields := strings.Fields(l)
|
|
if len(fields) < 2 {
|
|
continue
|
|
}
|
|
switch fields[0] {
|
|
case "pswpin":
|
|
value, err := strconv.ParseUint(fields[1], 10, 64)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
ret.Sin = value * 4 * 1024
|
|
case "pswpout":
|
|
value, err := strconv.ParseUint(fields[1], 10, 64)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
ret.Sout = value * 4 * 1024
|
|
}
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
func calcuateAvailVmem(ret *VirtualMemoryStat) uint64 {
|
|
var watermarkLow uint64
|
|
fn := common.HostProc("zoneinfo")
|
|
lines, _ := common.ReadLines(fn)
|
|
pagesize := uint64(os.Getpagesize())
|
|
watermarkLow = 0
|
|
|
|
for _, line := range lines {
|
|
fields := strings.Fields(line)
|
|
|
|
if strings.HasPrefix(fields[0], "low") {
|
|
lowValue, err := strconv.ParseUint(fields[1], 10, 64)
|
|
|
|
if err != nil {
|
|
lowValue = 0
|
|
}
|
|
|
|
watermarkLow += lowValue
|
|
}
|
|
}
|
|
|
|
watermarkLow *= pagesize
|
|
|
|
availMemory := ret.Free - watermarkLow
|
|
pageCache := ret.Active + ret.Inactive
|
|
pageCache -= uint64(math.Min(float64(pageCache/2), float64(watermarkLow)))
|
|
availMemory += pageCache
|
|
availMemory += ret.SReclaimable - uint64(math.Min(float64(ret.SReclaimable/2.0), float64(watermarkLow)))
|
|
|
|
if availMemory < 0 {
|
|
availMemory = 0
|
|
}
|
|
|
|
return availMemory
|
|
}
|