migrate_pages(2)

๐Ÿ’– migrate_pages(2) โ€” the migrate_pages system call, bestie edition ๐Ÿ’–

okay so we're in section 2 of the manual (system calls, the spicy stuff) and today's main character is migrate_pages. she moves memory around. she's iconic. let's get into it ๐Ÿฆ‹


โœจ NAME โœจ

migrate_pages โ€” move all pages in a process to another set of nodes


๐Ÿ“š LIBRARY ๐Ÿ“š

NUMA (Non-Uniform Memory Access) policy library โ€” that's libnuma, and you link it with -lnuma. ๐Ÿ’…


๐ŸŒธ SYNOPSIS ๐ŸŒธ

#include <numaif.h>

long migrate_pages(int pid, unsigned long maxnode,
                   const unsigned long *old_nodes,
                   const unsigned long *new_nodes);

๐Ÿ’ซ DESCRIPTION ๐Ÿ’ซ

so basically migrate_pages() is like... a glow-up for your memory. it attempts to move all pages of the process pid that live in the memory nodes listed in old_nodes over to the memory nodes listed in new_nodes. that's it, that's the whole vibe. โœจ

pages that aren't in any node of old_nodes? not migrated, not invited, not on the guest list. sorry babe. ๐Ÿ™ˆ

and here's the cute part โ€” as far as possible, the kernel maintains the relative topology relationship inside old_nodes during the migration to new_nodes. like, she keeps the friend group together when she moves them. queen behavior honestly ๐Ÿ‘‘

now, old_nodes and new_nodes are pointers to bit masks of node numbers, with up to maxnode bits in each mask. these masks are stored as arrays of unsigned long integers โ€” and in the last long integer, the bits beyond those specified by maxnode are ignored. so don't stress about the stragglers at the end, they don't count. ๐Ÿ’…

and maxnode is the maximum node number in the bit mask plus one. yep, plus one โ€” the off-by-one is real but it's honestly giving same energy as mbind(2), different from select(2) tho. don't mix them up, iykyk.

then there's pid: the ID of the process whose pages are to be moved. if you wanna move pages in another process, you've gotta be privileged โ€” that's CAP_SYS_NICE โ€” or your real or effective user ID has to match the real or saved-set user ID of the target process. no sneaking into other people's memory, bestie. ๐Ÿšซ

oh and if pid is 0? then migrate_pages() moves pages of the calling process. self-care!! ๐ŸŽ€

โš ๏ธ and listen โ€” pages shared with another process will be moved only if the initiating process has the CAP_SYS_NICE privilege. shared custody requires paperwork fr fr.

๐Ÿ”ฅ RETURN VALUE ๐Ÿ”ฅ

on success, migrate_pages() returns the number of pages that could not be moved โ€” so yeah, heads up, a return of zero means that all pages were successfully moved. zero is the slay. anything else is leftovers. ๐Ÿ’€

on error it returns -1, and sets errno to indicate the error. classic.


๐Ÿ“ ERRORS ๐Ÿ“

  • EFAULT โ€” part or all of the memory range specified by old_nodes/new_nodes and maxnode points outside your accessible address space. she went somewhere she wasn't allowed ๐Ÿ˜ญ
  • EINVAL โ€” the value specified by maxnode exceeds a kernel-imposed limit. or, old_nodes or new_nodes specifies one or more node IDs that are greater than the maximum supported node ID. or, none of the node IDs specified by new_nodes are on-line and allowed by the process's current cpuset context, or none of the specified nodes contain memory. so many ways to be invalid, pls pick one ๐Ÿ™ˆ
  • EPERM โ€” insufficient privilege (CAP_SYS_NICE) to move pages of the process specified by pid, or insufficient privilege (CAP_SYS_NICE) to access the specified target nodes. no badge, no entry ๐ŸŽ€
  • ESRCH โ€” no process matching pid could be found. she's not here. she ghosted. ๐Ÿ‘ป

โญ STANDARDS โญ

Linux. (just linux, bestie, nothing else claimed her) ๐Ÿ’œ


๐Ÿฆ‹ HISTORY ๐Ÿฆ‹

Linux 2.6.16. ancient. vintage. still serving looks. โœจ


๐ŸŒˆ NOTES ๐ŸŒˆ

for info on the library support, go peek at numa(7). ๐Ÿ“–

use get_mempolicy(2) with the MPOL_F_MEMS_ALLOWED flag to obtain the set of nodes that are allowed by the calling process's cpuset. but โ€” note that this information is subject to change at any time by manual or automatic reconfiguration of the cpuset. so don't get too attached, it's giving situationship. ๐Ÿ˜ญ

โš ๏ธ also real talk: use of migrate_pages() may result in pages whose location (node) violates the memory policy established for the specified addresses (see mbind(2)) and/or the specified process (see set_mempolicy(2)). that is, memory policy does not constrain the destination nodes used by migrate_pages(). she does what she wants. respectful queen but also chaotic. ๐Ÿ’…

and the <numaif.h> header is not included with glibc โ€” you've gotta install libnuma-devel or a similar package. go install it, i'll wait ๐Ÿซถ


๐Ÿ‘‘ SEE ALSO ๐Ÿ‘‘


okay that's the whole page babes, and real quick the sign-off: this gem is from Linux man-pages 6.20, dated 2026-02-08. read it, love it, move your pages responsibly ๐Ÿงš๐Ÿ’–โœจ

๐Ÿ“ฆ where this page comes from
  • arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
  • nixpkgs unstable: man-pages 6.20
  • ๐Ÿ“œ the page's license: Linux-man-pages-copyleft-2-para
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms