Skip to content

Commit 5b25504

Browse files
authored
[Linux] expose process locked memory (#2994)
Fixes #1109.
1 parent ec3df32 commit 5b25504

7 files changed

Lines changed: 31 additions & 1 deletion

File tree

docs/api.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1701,6 +1701,8 @@ Process class
17011701
+-------------+----------------+--------------------+
17021702
| hugetlb | | |
17031703
+-------------+----------------+--------------------+
1704+
| locked | | |
1705+
+-------------+----------------+--------------------+
17041706

17051707
Linux:
17061708

@@ -1721,6 +1723,8 @@ Process class
17211723
< 2.6.34.
17221724
- :field:`hugetlb`: resident memory backed by huge pages. Set to 0 on Linux
17231725
< 4.4.
1726+
- :field:`locked`: locked memory that cannot be
1727+
:term:`swapped out <swap-out>`. Typically locked via :manpage:`mlock(2)`.
17241728

17251729
macOS:
17261730

docs/changelog.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ Changelog
4949
no longer set to ``0``.
5050
- :gh:`2977`: new :func:`bytes2human` utility function, converting a number of
5151
bytes to a human-readable string (e.g. ``9.8K``).
52+
- :gh:`1109`, [Linux]: :meth:`Process.memory_extras` returns a new
53+
:field:`locked` field: locked virtual memory (:manpage:`mlock(2)`).
5254
- :gh:`1452`, :gh:`2993`, [Linux, macOS, Windows]:
5355
:meth:`Process.memory_footprint` returns a new :field:`shared` field:
5456
resident memory shared with other processes.

docs/migration.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ New memory_extras() method
167167
platform-specific memory metrics which complement :meth:`Process.memory_info`:
168168

169169
- Linux: :field:`peak_rss`, :field:`peak_vms`, :field:`rss_anon`,
170-
:field:`rss_file`, :field:`rss_shmem`, :field:`swap_anon`, :field:`hugetlb`.
170+
:field:`rss_file`, :field:`rss_shmem`, :field:`swap_anon`, :field:`hugetlb`,
171+
:field:`locked`.
171172
- macOS: :field:`phys_footprint`, :field:`peak_footprint`.
172173
- Windows: :field:`virtual`, :field:`peak_virtual`, :field:`paged_pool`,
173174
:field:`nonpaged_pool`, :field:`peak_paged_pool`,

psutil/_ntuples.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ class pmem_extras(NamedTuple):
420420
rss_shmem: int
421421
swap_anon: int
422422
hugetlb: int
423+
locked: int
423424

424425
# psutil.Process().memory_full_info()
425426
pfullmem = namedtuple("pfullmem", pmem._fields + ("uss", "pss", "swap"))

psutil/_pslinux.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1915,6 +1915,7 @@ def memory_extras(
19151915
_rssshmem_re=re.compile(br"RssShmem:\s+(\d+)"),
19161916
_vmswap_re=re.compile(br"VmSwap:\s+(\d+)"),
19171917
_hugetlb_re=re.compile(br"HugetlbPages:\s+(\d+)"),
1918+
_vmlck_re=re.compile(br"VmLck:\s+(\d+)"),
19181919
):
19191920
# Read /proc/{pid}/status.
19201921
# RssAnon/RssFile/RssShmem were added in Linux 4.5;
@@ -1933,6 +1934,7 @@ def parse(regex):
19331934
rss_shmem=parse(_rssshmem_re),
19341935
swap_anon=parse(_vmswap_re),
19351936
hugetlb=parse(_hugetlb_re),
1937+
locked=parse(_vmlck_re),
19361938
)
19371939

19381940
if HAS_PROC_SMAPS_ROLLUP or HAS_PROC_SMAPS:

tests/test_linux.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import collections
1010
import contextlib
11+
import ctypes
1112
import errno
1213
import glob
1314
import io
@@ -2551,6 +2552,24 @@ def test_memory_extras(self):
25512552
vmrss = int(re.search(br"VmRSS:\s+(\d+)", data).group(1)) * 1024
25522553
assert mem.rss_anon + mem.rss_file + mem.rss_shmem == vmrss
25532554

2555+
def test_memory_extras_locked(self):
2556+
libc = ctypes.CDLL(None, use_errno=True)
2557+
size = mmap.PAGESIZE * 4
2558+
buf = mmap.mmap(-1, size)
2559+
self.addCleanup(buf.close)
2560+
view = ctypes.c_char.from_buffer(buf)
2561+
addr = ctypes.addressof(view)
2562+
del view
2563+
base = psutil.Process().memory_extras().locked
2564+
if libc.mlock(ctypes.c_void_p(addr), ctypes.c_size_t(size)) != 0:
2565+
err = ctypes.get_errno()
2566+
return pytest.skip(f"mlock() failed: {os.strerror(err)}")
2567+
self.addCleanup(
2568+
libc.munlock, ctypes.c_void_p(addr), ctypes.c_size_t(size)
2569+
)
2570+
after = psutil.Process().memory_extras().locked
2571+
assert after - base == size
2572+
25542573
def test_memory_footprint_shared_self_mappings(self):
25552574
# A page mapped twice by the same process counts as shared,
25562575
# even with no other process involved.

tests/test_process.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,7 @@ def test_memory_extras_fields_order(self):
505505
"rss_shmem",
506506
"swap_anon",
507507
"hugetlb",
508+
"locked",
508509
)
509510
elif MACOS:
510511
assert mem._fields == ("phys_footprint", "peak_footprint")

0 commit comments

Comments
 (0)