Skip to content

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