Move a Process's NUMA Pages with migratepages
You will finish with a controlled way to move a process's physical memory pages between NUMA nodes, while leaving its virtual addresses unchanged. You will also know how to inspect placement before changing it and how to avoid treating a successful command as proof that the process is now faster. The examples use migratepages from numactl 2.0.18-1ubuntu0.24.04.1, installed on this machine.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes for a read-only inspection and a smoke test. A real migration needs a process you own, a clear maintenance window, and enough free memory on the destination nodes. You need a NUMA-aware kernel with page-migration support. Moving pages changes live process memory placement, so do not start with a production service.
1. Confirm the command's contract
migratepages takes exactly three arguments: a process ID, a source node set, and a destination node set. Ask the installed binary for its usage text:
$ command -v migratepages
/usr/bin/migratepages
$ migratepages --help
usage: migratepages pid from-nodes to-nodes
nodes is a comma delimited list of node numbers or A-B ranges or all.
The --help argument is not a documented option. This version prints usage and exits with status 1 because it expects a PID and two node specifications. Use the command shape above when writing a real invocation.
Checkpoint: confirm the package version before comparing output with another host:
$ dpkg-query -W -f='\${Package} \${Version}\n'
numactl 2.0.18-1ubuntu0.24.04.1
2. Inspect the process before moving anything
Choose a target process and record its current NUMA map. The map is exposed through /proc/<pid>/numa_maps. Reading it is ordinary, non-destructive inspection:
$ PID='12345'
$ sed -n '1,12p' "/proc/$PID/numa_maps"
00400000 default file=/usr/bin/example mapped=4 N0=4 kernelpagesize_kB=4
...
Replace 12345 with a real PID. Do not copy the sample map as expected output: file paths, mapping counts and node numbers depend on the process and host. Entries such as N0=4 report pages currently on node 0. An anonymous mapping can show a different node count from a file-backed mapping.
Checkpoint: verify that the process still exists immediately before any migration:
$ test -r "/proc/$PID/numa_maps" && printf 'NUMA map is readable for PID %s\n' "$PID"
NUMA map is readable for PID 12345
If this fails, the process may have exited, or its proc access may be restricted. Re-select the PID rather than applying the command to a different process by accident.
3. Understand node specifications
Node sets can name every node with all, one node with a number, several nodes with commas, or a range such as 2-5. Prefix a specification with ! to invert the selection. Keep the two sets separate: the source set says which pages are eligible to move, while the destination set says where the command should try to place them.
# Examples of node-set syntax. These lines are illustrative only.
all # every NUMA node
0 # node 0
0,2,4 # three nodes
2-5 # nodes 2, 3, 4 and 5
!0 # every node except node 0
The command tries to preserve relative placement when both sets contain multiple nodes. For example, a source range and a destination list may be paired in order. That preferred pairing is only possible when the destination has enough memory. Treat the result as an attempt, not a promise that every page moved to the node you had in mind.
4. Run a reversible smoke test on a process you own
For a first test, use your own shell and a narrow destination. This changes physical page placement for that shell, but does not rewrite its virtual address space or persistent configuration:
$ migratepages $$ all 0
$ printf 'migratepages status: %s\n' "$?"
migratepages status: 0
On this machine the successful command is silent and returns status 0. The example is only suitable where node 0 exists. If the host has no node 0, stop and choose a node shown by the host's NUMA tools. Do not paste node numbers from another machine.
There is no persistent undo operation because the command changes current page placement, not a policy file. To move the pages again, run another migration with the desired source and destination sets. Pages may be faulted in again later, so the observed placement can change as the process runs.
Warning: never use a broad migration on a busy service merely because the command is short. Moving a large working set can consume memory bandwidth, compete with the workload, and produce a worse placement. Stop the test process or restore its workload before judging performance.
5. Migrate a selected process after checking permissions
For a real process, replace both placeholders with values from the same host:
$ PID='12345'
$ FROM_NODES='2-5'
$ TO_NODES='7,9,12-13'
$ migratepages "$PID" "$FROM_NODES" "$TO_NODES"
$ printf 'migration status: %s\n' "$?"
migration status: 0
Do not run this unchanged: the node numbers are examples, and the PID is a placeholder. Confirm that every node exists and that the destination has capacity. A zero status says the request was accepted; inspect /proc/$PID/numa_maps again to see what actually moved.
An ordinary user can move pages that are not shared with other processes, provided that user has the right to modify the target process. Pages shared with other processes require administrative privilege. Root, or another user with the relevant administrative privilege, can move all pages. Use elevated privileges only for a process and maintenance window you have explicitly identified:
$ sudo migratepages "$PID" "$FROM_NODES" "$TO_NODES"
$ printf 'migration status: %s\n' "$?"
migration status: 0
Do not use sudo to hide an incorrect PID, a missing node or an unreadable map. Verify the target first. A privileged command can alter shared pages for other processes and can affect a wider workload than the one you intended.
6. Diagnose a failed or ineffective move
Capture the status immediately and read the diagnostic. For example, a nonexistent PID fails safely:
$ migratepages 999999 all 0
migrate_pages: No such process
$ printf 'status: %s\n' "$?"
status: 1
A failure can mean that the process ended, the source or destination specification is invalid, the kernel lacks page-migration support, the request needs more privilege, or the destination cannot satisfy the move. Re-read the target's NUMA map and check the host's available nodes before retrying. Do not infer success from silence alone; check the exit status.
If the command succeeds but the map changes little, that can be normal. Some pages are shared, pinned, already on a suitable node, or newly allocated after the command ran. Compare relevant mappings before and after, then measure the workload under the same conditions. migratepages changes placement; it does not set CPU affinity, change a NUMA policy, or guarantee lower latency.
Done means
- You confirmed the installed numactl version and the three-argument syntax.
- You inspected
/proc/<pid>/numa_mapsand rechecked the PID before acting. - You used node sets that exist on this host, not copied numbers from a different machine.
- You distinguished an ordinary user's limited migration from a privileged migration of shared pages.
- You checked the exit status and inspected NUMA placement after the request.