What is Accurate Memory With USS PSS in Linux-Free Embedded Linux Course In Hyderabad

PREV_LEC | NEXT_LEC

Accurate Memory With USS PSS

Fixing RSS’s shared-page blind spot on embedded Linux — free linux device drivers course

In the previous lecture of this free linux kernel development course we saw that summing RSS across processes overstates real memory usage because shared library pages get counted once per process. This lecture introduces the two metrics that fix that: USS (unique set size) and PSS (proportional set size), and the smem tool that reports them from /proc/<PID>/smaps.

Keywords covered

USS PSS smem /proc/PID/smaps free linux device drivers course

What You Will Learn

  • Why RSS alone cannot answer “how much total RAM is in use”
  • What USS and PSS mean, with a worked shared-library example
  • How to read /proc/<PID>/smaps by hand
  • How to install and run smem — and the lightweight alternative for targets without Python
  • An original two-process demo that proves PSS adds up correctly across processes

Prerequisites

This lecture builds directly on VSS/RSS from the previous lecture. You will need a target (or VM) where you can install Python 3, since smem is a Python tool.

The Shared-Page Problem, Restated

Picture six independent daemons on a board, all dynamically linked against the same libc. Each process’s RSS includes every page of libc that it has touched. If you naively add up six RSS values, you count that libc code six times over, even though it physically exists in RAM exactly once. On a small embedded board this error can be the difference between “we have headroom” and “we are about to hit OOM” in your own capacity planning.

USS: Unique Set Size

USS is the memory that is resident and private to one process — not shared with anything else. It is exactly the memory that would be freed the instant that process exited. USS is the most conservative, “worst case if this process dies” number, and it is immune to the shared-library double-counting problem by definition, since shared pages are excluded from it entirely.

PSS: Proportional Set Size

PSS solves the double-counting problem differently: instead of excluding shared pages, it divides each shared page’s cost evenly across every process mapping it. If a 12-page library segment is shared by six processes, each process’s PSS gains exactly 2 pages for that mapping. Sum the PSS of every process on the system and you get the true total physical memory in use — no over- or under-counting.

USS vs PSS For A Shared Library
libc.so mapped by 6 processes, 12 physical pages total RSS view (wrong total if summed): P1 RSS +=12 P2 RSS +=12 … P6 RSS +=12 -> sums to 72 pages (WRONG, only 12 exist) USS view (excludes shared pages): P1 USS +=0 P2 USS +=0 … P6 USS +=0 -> shared pages invisible to USS PSS view (splits fairly): P1 PSS +=2 P2 PSS +=2 … P6 PSS +=2 -> sums to 12 pages (CORRECT)

Reading /proc/PID/smaps By Hand

Every mapped region in a process gets its own block in smaps, with Rss and Pss already broken out per-region:

$ grep -A8 'libc' /proc/$$/smaps | head -9
7f2a1c000000-7f2a1c1c0000 r-xp 00000000 08:01 123456  /usr/lib/x86_64-linux-gnu/libc.so.6
Size:               1792 kB
Rss:                 620 kB
Pss:                  38 kB
Shared_Clean:        620 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         0 kB

This single region shows Rss of 620 kB but Pss of only 38 kB — the gap is exactly the shared-page effect: most of this shell’s mapped libc pages are also resident in dozens of other processes on the box.

Installing And Running smem

smem collates every process’s smaps file and totals USS/PSS/RSS in one table:

# On the target (needs Python 3):
$ sudo apt-get install smem     # desktop/Debian-based rootfs
$ smem -t -k
  PID User     Command                         Swap   USS   PSS   RSS
  412 root     ep-uniqdemo                        0    892   918  1204
  588 root     ep-shareddemo                       0    120   946  1832
  591 root     ep-shareddemo                       0    120   946  1832
------------------------------------------------------------
                                                    0   1132  2810  4868

Note the last line: the sum of PSS (2810) is much closer to real physical usage than the sum of RSS (4868) would suggest.

Many embedded rootfs images cannot afford a full Python environment just for one tool. For that case, capture the raw state on-target with smemcap (part of BusyBox) and analyze it later on your desktop:

# On the target (no Python needed):
# smemcap > smem-board-cap.tar

# Copy the tar to your host, then:
$ smem -t -S smem-board-cap.tar

Original Demo: Two Processes, One Shared Library

This original demo builds a small shared library and two processes: one that links it and uses it heavily (creating private, unshared heap allocations too), and one that just links it and idles — so you can see USS diverge while PSS reflects fair sharing.

/* ep_shared_lib.c - a tiny "shared" library, deliberately built as .so */
#include <string.h>

static char pad[512 * 1024]; /* forces real pages once touched */

void ep_touch_shared(void)
{
    memset(pad, 0x11, sizeof(pad));
}
/* ep_uniqdemo.c - links the shared lib AND allocates a lot privately */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

extern void ep_touch_shared(void);

int main(void)
{
    ep_touch_shared();
    char *priv = malloc(2 * 1024 * 1024);
    memset(priv, 0x22, 2 * 1024 * 1024); /* private, unique memory */
    printf("ep_uniqdemo pid=%d ready\n", getpid());
    pause();
    return 0;
}
$ gcc -fPIC -shared -o libepshared.so ep_shared_lib.c
$ gcc -o ep_uniqdemo ep_uniqdemo.c -L. -lepshared -Wl,-rpath,.
$ LD_LIBRARY_PATH=. ./ep_uniqdemo &
$ LD_LIBRARY_PATH=. ./ep_uniqdemo &   # second copy, shares the .so

Expected smem output — USS differs little between the two copies (each has its own private 2 MB), but the shared library’s contribution shows up fairly split in PSS rather than doubled in RSS:

$ smem -t -k -P ep_uniqdemo
  PID User     Command        Swap    USS    PSS    RSS
 2201 pi       ep_uniqdemo       0   2148   2402   2860
 2202 pi       ep_uniqdemo       0   2148   2402   2860

USS vs PSS vs RSS At A Glance

MetricIncludes shared pages?Best use
RSSYes, fully, per processQuick single-process check; do not sum across processes
USSNo — private only“What would I free if I killed this process right now”
PSSYes, fairly dividedAccurate system-wide total memory in use

Other Tools Worth Knowing

If smem is unavailable, ps_mem reports similar PSS-based numbers in a simpler format, and on Android-derived systems procrank reports the same class of data and can be cross-compiled for generic embedded Linux with minor changes.

Common Mistakes

  • Summing RSS instead of PSS when estimating total system memory use — always sum PSS, never RSS, across processes.
  • Assuming USS is “total” memory — it deliberately excludes shared pages and will understate a process’s real footprint if shared libraries dominate.
  • Installing full smem on a tiny rootfs when smemcap + host-side analysis would avoid pulling in a Python dependency.

Best Practices

  • Report USS and PSS together for any process you are investigating — USS for “what frees on exit”, PSS for “fair system-wide share”.
  • Use smemcap on production images to keep Python off the target, and analyze the capture on your development host.
  • When comparing memory usage between builds, always compare PSS totals, not RSS totals — RSS totals are not stable across changes in the number of processes.

Summary And Key Takeaways

RSS alone cannot be summed meaningfully across processes because shared pages get counted repeatedly. USS gives you the safe, private-only floor; PSS gives you the fair, summable total. smem (or its lightweight smemcap companion) reads this directly out of /proc/<PID>/smaps.

Conclusion

Once you can read USS and PSS confidently, you have a trustworthy, summable picture of memory usage across an entire embedded system — a core skill for anyone progressing through a free linux kernel development course toward real board bring-up and long-running daemon debugging.

FAQ

What does USS stand for and what does it mean?

USS is Unique Set Size — the memory resident and private to one process, which would be freed if that process exited.

What does PSS stand for and what does it mean?

PSS is Proportional Set Size — shared pages are divided fairly across every process that maps them, so summing PSS across processes gives the correct system-wide total.

Why is summing RSS across processes wrong?

Shared library pages are counted in full for every process that maps them, inflating the summed total far beyond actual physical memory used.

Do I need Python to use smem on an embedded target?

Not necessarily — use smemcap (part of BusyBox) to capture /proc state on the target, then analyze the capture with smem on your development host.

Where does smem get its data from?

It reads /proc/PID/smaps, which breaks Rss and Pss down per memory mapping for each process.

Is PSS always smaller than RSS?

PSS is less than or equal to RSS for any process with shared mappings, and equal to RSS for a process with no shared pages at all.

What is the lightweight alternative to smem?

ps_mem reports similar PSS-based numbers in a simpler format without needing the full smem package.

Continue The Free Linux Device Drivers Course

Next up: finding memory leaks with mtrace — a glibc-native leak tracer.

Next Lecture Course Index
PREV_LEC | NEXT_LEC

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *