Expose shared memory and private (unshared) memory use on Linux separately
Using ps
from procps-ng
on a Linux machine there does not appear to be a way to reliably determine a process's private anonymous and/or file-backed memory use.
The Linux kernel exposes the necessary information in the /proc/$pid/statm
fields resident
and shared
. Subtracting the shared
amount from the resident
amount will generally report the amount of private memory the process is using. But ps
does not appear to expose the shared
field, nor synthesize a private memory field from it. Nor does it expose the proportional shared memory accounting information in /proc/$pid/smaps_rollups
as an alternative.
As a result it's awfully hard to find out which processes on your system are actually using the most RAM when shared memory is used heavily. (This issue also affects top
).
This has been a longstanding ps
limitation, and I'd like to patch ps
to directly support showing separate private- and shared- memory use for processes, and to sort by those fields.
The documentation warns that proposed changes should be raised for discussion before going straight to writing a patch. So here I am.
I think it's probably sufficient to provide new output- and sort-columns for private memory and shared memory. Shared memory would be the 4th field in /proc/$pid/statm
and private memory would be the 2nd field (resident) minus the 4th field (shared).
This still needs some validation against /proc/$pid/smaps_rollup
in a fork()-without-exec() test program and a program that uses POSIX shmem to ensure that it behaves sensibly for both CoW page mappings after fork() and for shmem segment and page mappings. It is somewhat complicated by Linux's lazy mapping of shmem pages into processes only when they're first accessed. This interacts in some possibly odd ways with Linux's proportional shared memory map accounting in the Pss_Shmem
field.
I wrote the following awk
script to find the private memory use of a list of pids as a workaround:
BEGIN {
printf "%10s %10s %10s %10s %10s %10s (all in KiB)\n", "pid", "size", "resident", "shared", "binsize", "private";
}
{
pid = $1
getline cmdline < sprintf("/proc/%d/cmdline", pid)
getline < sprintf("/proc/%d/statm", pid)
size = $1 * 4;
resident = $2 * 4;
shared = $3 * 4;
text=$4 * 4;
printf "%10d %10d %10d %10d %10d %10d %s\n", pid, size, resident, shared, text, (resident - shared), cmdline;
}
and here's a test program I've been using when experimenting with /proc/$pid/smaps
and /proc/$pid/smaps_rollup