summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2016-09-20 15:14:24 +0200
committerGiampaolo Rodola <g.rodola@gmail.com>2016-09-20 15:14:24 +0200
commit8504d930cb68e1e079e03df55112312505ed260f (patch)
treeac5916ba5a77083b9a99cf124906bcc45414057c
parentc4cad5a96f1d4c6d383d51e0da2344cedcf7028e (diff)
downloadpsutil-8504d930cb68e1e079e03df55112312505ed260f.tar.gz
issue #887: calculate avail mem on older kernels where MemAvailable
column is not available.
-rw-r--r--docs/index.rst31
-rw-r--r--psutil/_pslinux.py123
-rwxr-xr-xscripts/top.py3
3 files changed, 107 insertions, 50 deletions
diff --git a/docs/index.rst b/docs/index.rst
index d64ab43a..675c028b 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -172,24 +172,27 @@ Memory
.. function:: virtual_memory()
Return statistics about system memory usage as a namedtuple including the
- following fields, expressed in bytes:
-
- - **total**: total physical memory available.
- - **available**: the actual amount of available memory that can be given
- instantly to processes that request more memory in bytes; this is
- calculated by summing different memory values depending on the platform
- and it is supposed to be used to monitor actual memory usage in a cross
- platform fashion.
+ following fields, expressed in bytes.
+ Main metrics:
+
+ - **total**: total physical memory.
+ - **available**: the memory that can be given instantly to processes without
+ the system going into swap.
+ This is calculated by summing different memory values depending on the
+ platform and it is supposed to be used to monitor actual memory usage in a
+ cross platform fashion.
- **percent**: the percentage usage calculated as
``(total - available) / total * 100``.
- - **used**: memory used, calculated differently depending on the platform and
- designed for informational purposes only.
- - **free**: memory not being used at all (zeroed) that is readily available;
- note that this doesn't reflect the actual memory available (use 'available'
- instead).
- Platform-specific fields:
+ Other metrics:
+ - **used**: memory used, calculated differently depending on the platform and
+ designed for informational purposes only. ``total - used`` does not
+ necessarily matches ``available``.
+ - **free**: memory not being used at all (zeroed) that is readily available;
+ note that this doesn't reflect the actual memory available (use
+ ``available`` instead). ``total - free`` does not necessarily match
+ ``used``.
- **active** *(UNIX)*: memory currently in use or very recently used, and so
it is in RAM.
- **inactive** *(UNIX)*: memory that is marked as not used.
diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py
index 1a0b57bc..a9606fc5 100644
--- a/psutil/_pslinux.py
+++ b/psutil/_pslinux.py
@@ -288,16 +288,71 @@ except Exception:
# =====================================================================
-def virtual_memory():
- """Report memory stats trying to match "free" and "vmstat -s" cmdline
- utility values as much as possible.
+def calculate_avail_vmem(mems):
+ """Fallback for kernels < 3.14 where /proc/meminfo does not provide
+ "MemAvailable:" column (see: https://blog.famzah.net/2014/09/24/)
+ trying to reimplement the algorithm outlined here:
+ https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
+ commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773
+
+ XXX: on my machine (Ubuntu 16.04, kernel 4.4.0.36, 16B ram) this
+ calculation differs by 1% than "MemAvailable:".
+ It is still way more realistic than doing (free + cached) though.
+ """
+ # Fallback for very old distros. According to
+ # https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
+ # commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773
+ # ...long ago "avail" was calculated as (free + cached).
+ # We might fallback in such cases:
+ # "Active(file)" not available: 2.6.28 / Dec 2008
+ # "Inactive(file)" not available: 2.6.28 / Dec 2008
+ # "SReclaimable:" not available: 2.6.19 / Nov 2006
+ # /proc/zoneinfo not available: 2.6.13 / Aug 2005
+ free = mems[b'MemFree:']
+ fallback = free + mems.get(b"Cached:", 0)
+ try:
+ lru_active_file = mems[b'Active(file):']
+ lru_inactive_file = mems[b'Inactive(file):']
+ slab_reclaimable = mems[b'SReclaimable:']
+ except KeyError:
+ return fallback
+ try:
+ f = open_binary('%s/zoneinfo' % get_procfs_path())
+ except IOError:
+ return fallback # kernel 2.6.13
+
+ watermark_low = 0
+ with f:
+ for line in f:
+ line = line.strip()
+ if line.startswith(b'low'):
+ watermark_low += int(line.split()[1])
+ watermark_low *= PAGESIZE
+ watermark_low = watermark_low
+
+ avail = free - watermark_low
+ pagecache = lru_active_file + lru_inactive_file
+ pagecache -= min(pagecache / 2, watermark_low)
+ avail += pagecache
+ avail += slab_reclaimable - min(slab_reclaimable / 2.0, watermark_low)
+ return int(avail)
+
- This implementation uses procps-ng-3.3.12 as a reference (2016-09-18):
+def virtual_memory():
+ """Report virtual memory stats.
+ This implementation matches "free" and "vmstat -s" cmdline
+ utility values and procps-ng-3.3.12 source was used as a reference
+ (2016-09-18):
https://gitlab.com/procps-ng/procps/blob/
24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c
-
For reference, procps-ng-3.3.10 is the version available on Ubuntu
16.04.
+
+ Note about "available" memory. Up until psutil 4.3 it was
+ calculated as "avail = (free + buffers + cached)". Now
+ "MemAvailable:" column (kernel 3.14) from /proc/meminfo is used as
+ it's more accurate.
+ That matches "available" column in newer versions of "free".
"""
missing_fields = []
mems = {}
@@ -306,20 +361,25 @@ def virtual_memory():
fields = line.split()
mems[fields[0]] = int(fields[1]) * 1024
- # Note: these info are available also as cext.linux_sysinfo().
+ # /proc doc states that the available fields in /proc/meminfo vary
+ # by architecture and compile options, but these 3 values are also
+ # returned by sysinfo(2); as such we assume they are always there.
total = mems[b'MemTotal:']
free = mems[b'MemFree:']
buffers = mems[b'Buffers:']
- cached = mems[b"Cached:"]
- # "free" cmdline utility sums cached + reclamaible (available
- # since kernel 2.6.19):
- # https://gitlab.com/procps-ng/procps/
- # blob/195565746136d09333ded280cf3ba93853e855b8/proc/sysinfo.c#L761
- # Older versions of procps added slab memory instead.
- # This got changed in:
- # https://gitlab.com/procps-ng/procps/commit/
- # 05d751c4f076a2f0118b914c5e51cfbb4762ad8e
- cached += mems.get(b"SReclaimable:", 0)
+
+ try:
+ cached = mems[b"Cached:"]
+ except KeyError:
+ cached = 0
+ missing_fields.append('cached')
+ else:
+ # "free" cmdline utility sums reclaimable to cached.
+ # Older versions of procps used to add slab memory instead.
+ # This got changed in:
+ # https://gitlab.com/procps-ng/procps/commit/
+ # 05d751c4f076a2f0118b914c5e51cfbb4762ad8e
+ cached += mems.get(b"SReclaimable:", 0) # since kernel 2.6.19
try:
shared = mems[b'Shmem:'] # since kernel 2.6.32
@@ -339,8 +399,6 @@ def virtual_memory():
try:
inactive = mems[b"Inactive:"]
except KeyError:
- # https://gitlab.com/procps-ng/procps/blob/
- # 195565746136d09333ded280cf3ba93853e855b8/proc/sysinfo.c#L758
try:
inactive = \
mems[b"Inact_dirty:"] + \
@@ -350,31 +408,28 @@ def virtual_memory():
inactive = 0
missing_fields.append('inactive')
- # https://gitlab.com/procps-ng/procps/blob/
- # 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L769
used = total - free - cached - buffers
if used < 0:
# May be symptomatic of running within a LCX container where such
# values will be dramatically distorted over those of the host.
used = total - free
- # Note: starting from 4.4.0 we match "free" "available" column.
- # Before 4.4.0 we calculated it as:
- # >>> avail = free + buffers + cached
- # ...which matched htop.
- # free and htop available memory differs as per:
- # http://askubuntu.com/a/369589
- # http://unix.stackexchange.com/a/65852/168884
+ # - starting from 4.4.0 we match free's "available" column.
+ # Before 4.4.0 we calculated it as (free + buffers + cached)
+ # which matched htop.
+ # - free and htop available memory differs as per:
+ # http://askubuntu.com/a/369589
+ # http://unix.stackexchange.com/a/65852/168884
+ # - MemAvailable has been introduced in kernel 3.14
try:
avail = mems[b'MemAvailable:']
except KeyError:
- # Column is not there; it's likely this is an older kernel.
- # In this case "free" won't show an "available" column.
- # Also, procps does some hacky things:
- # https://gitlab.com/procps-ng/procps/blob/
- # /24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L774
- # We won't. Like this we'll match "htop".
- avail = free + buffers + cached
+ avail = calculate_avail_vmem(mems)
+
+ if avail < 0:
+ avail = 0
+ missing_fields.append('available')
+
# If avail is greater than total or our calculation overflows,
# that's symptomatic of running within a LCX container where such
# values will be dramatically distorted over those of the host.
diff --git a/scripts/top.py b/scripts/top.py
index 1caa8136..0c99047e 100755
--- a/scripts/top.py
+++ b/scripts/top.py
@@ -137,11 +137,10 @@ def print_header(procs_status, num_procs):
perc))
mem = psutil.virtual_memory()
dashes, empty_dashes = get_dashes(mem.percent)
- used = mem.total - mem.available
line = " Mem [%s%s] %5s%% %6s/%s" % (
dashes, empty_dashes,
mem.percent,
- str(int(used / 1024 / 1024)) + "M",
+ str(int(mem.used / 1024 / 1024)) + "M",
str(int(mem.total / 1024 / 1024)) + "M"
)
print_line(line)