Read Per-Process NUMA Placement from /proc
You will finish with a repeatable way to inspect one process's NUMA memory policy, mapping usage and page placement. The workflow is read-only: it does not move pages, change policy or restart a service. Allow about ten minutes. You need a Linux shell and a process ID; the examples use the installed Linux man-pages 6.7 package on a 6.8 kernel.
The route
Jump straight to the step you need, or tick off Done means at the end.
The file is /proc/PID/numa_maps. It has existed since Linux 2.6.14. The local proc_pid_numa_maps(5) page is deliberately brief and points to numa(7); the kernel's proc documentation defines the output shape used below.
1. Choose a process without changing it
Use a process that will remain alive while you inspect it. For a first test, inspect the shell running the command:
$ printf 'PID=%s\n' "$$"
PID=24831
$ ps -p "$$" -o pid=,comm=
24831 bash
The number is host-specific. Replace PID in later commands with a real decimal PID, or keep using $$ for the current shell. Reading your own process normally needs no elevated privileges. Reading another user's process may be restricted by procfs permissions or by the host's security settings.
Checkpoint: confirm that the process exists before reading its NUMA view:
$ test -d /proc/"$PID" && echo 'process exists'
process exists
If the check fails, the process exited or the PID was mistyped. Do not reuse a PID obtained much earlier: Linux can recycle it.
2. Read the mapping report
Set a placeholder to a PID you selected, then read the first few lines:
$ PID=24831
$ sed -n '1,8p' "/proc/$PID/numa_maps"
64a1e8c43000 default file=/usr/bin/sed mapped=3 active=0 N0=3 kernelpagesize_kB=4
64a1e8c46000 default file=/usr/bin/sed mapped=19 active=0 N0=19 kernelpagesize_kB=4
64a1e8c59000 default file=/usr/bin/sed mapped=4 active=0 N0=4 kernelpagesize_kB=4
Your addresses, executable paths and counters will differ. Each line describes one memory mapping and begins with its starting virtual address. The next field is the effective NUMA policy for that mapping. The remaining fields describe the mapping and its pages.
There is no promise that every line contains every counter. A mapping with no resident pages may show less information than one with pages allocated. Treat the output as a snapshot, not as a permanent inventory.
3. Relate an entry to /proc/PID/maps
The first field is useful because it is the start address used by the process's normal maps report. Display both files when you need to identify a library or anonymous region:
$ sed -n '1,5p' "/proc/$PID/maps"
64a1e8c43000-64a1e8c46000 r--p 00000000 08:01 123456 /usr/bin/sed
64a1e8c46000-64a1e8c59000 r-xp 00003000 08:01 123456 /usr/bin/sed
$ sed -n '1,3p' "/proc/$PID/numa_maps"
64a1e8c43000 default file=/usr/bin/sed mapped=3 active=0 N0=3 kernelpagesize_kB=4
The sample addresses are illustrative and will not match your machine. Match the starting address, then use the pathname and the permissions from maps to identify the region. Do not infer that a pathname alone identifies one mapping: a file can have several ranges and separate anonymous portions.
4. Read the fields that answer common questions
Use the fields as evidence for a specific question rather than trying to turn the file into a single memory total.
default,bind,interleaveor another policy word describes the mapping's NUMA memory policy. A policy is not the same thing as a guarantee that every page is currently on one node.file=...,anonandstackidentify the broad mapping type.hugeindicates huge-page backing in the kernel's example format.mapped=counts mapped pages reported for the range, whileanon=anddirty=describe page categories. These are page counts, not kilobytes.active=reports pages considered active during the scan. It is not a CPU utilisation measurement.N0=,N1=and so on count pages allocated on each NUMA node. The node numbers are machine-specific, so do not assume that node 0 is the fastest or nearest node.kernelpagesize_kB=tells you the backing page size in kilobytes for that mapping. It is not a conversion factor that makes every other counter a byte count.
mapmax=, when present, is the largest mapping count observed for a page in the range. Some kernel configurations cannot track the exact count for a larger allocation, so do not treat it as a precise per-page value in every build.
5. Make a small, reproducible check
To list only the lines that include node counters, use an ordinary unprivileged pipeline:
$ awk '/(^| )N[0-9]+=/{print}' "/proc/$PID/numa_maps" | sed -n '1,5p'
64a1e8c43000 default file=/usr/bin/sed mapped=3 active=0 N0=3 kernelpagesize_kB=4
64a1e8c46000 default file=/usr/bin/sed mapped=19 active=0 N0=19 kernelpagesize_kB=4
An empty result can be legitimate, for example if the selected process has no reported resident pages in the ranges scanned. It can also mean that the process exited between commands. Check test -d /proc/$PID again before diagnosing NUMA behaviour.
For a quick node-level view, extract just the node counters. This keeps each token intact for later review:
$ awk '{for (i = 1; i <= NF; i++) if ($i ~ /^N[0-9]+=/) printf "%s ", $i; print ""}' "/proc/$PID/numa_maps" | sed -n '1,8p'
N0=3
N0=19
N0=4
6. Avoid the traps
Do not write to this file. The NUMA report is a read-only observation interface; changing placement requires other interfaces and a deliberate policy decision. Nothing in this guide needs sudo. Adding it can obscure an access-control problem and can make you inspect a different procfs view in a container or namespace.
Reading the file causes the kernel to scan the process's virtual address space. It can therefore cost more than reading a small status file, especially for a large process, and the process may keep allocating or freeing memory while the scan runs. Repeat the read if you need to see whether a pattern persists. Do not add a destructive page migration or policy change to a diagnostic script without a rollback and a maintenance window.
Finally, keep kernel versions in view. The installed manpage is from Linux man-pages 6.7, while the output is produced by the running kernel. Field availability and exact semantics can vary with kernel configuration and release. Record uname -r beside captured output when comparing hosts:
$ uname -r
6.8.0-139-generic
$ printf 'captured PID: %s\n' "$PID"
captured PID: 24831
Done means
- You selected a live PID and confirmed that procfs still exposes it.
- You read
/proc/PID/numa_mapswithout changing process or system state. - You matched a NUMA entry's starting address with
/proc/PID/maps. - You interpreted node counters as page counts and kept node numbering host-specific.
- You recorded the kernel version when the output needed to be compared or retained.