$RodHat_
Syscall reference · Memory

process_madvise(2)

madvise() for another process's address space, addressed by pidfd and an iovec of ranges.

Since
Linux 5.10check your kernel →
glibc
glibc 2.36
System
Linux
Man page
process_madvise(2)

Synopsis

#include <sys/mman.h>

ssize_t process_madvise(int pidfd, const struct iovec iovec[n],
                        size_t n, int advice, unsigned int flags);

Rod's take

madvise only works on memory you own, which is useless when you're the supervisor staring at someone else's bloated process. process_madvise takes a pidfd and an iovec of ranges inside the target, so a memory daemon or checkpoint tool can say cold or page-this-out from outside. Android's low memory killer does exactly that before it pulls the trigger. The pidfd means PID reuse can't trick you into advising the wrong process. Check the return value; it's bytes advised and it can be partial. And read the permissions section: CAP_SYS_NICE on top of ptrace read access.

Flags and arguments that matter

NameWhat it does
MADV_COLDDeactivate the target's pages so they age toward reclaim. Valid on another process.
MADV_PAGEOUTReclaim the target's pages now (anon to swap, dirty file pages written back). Valid on another process.
MADV_WILLNEEDExpect access soon; read ahead. Valid on another process.
MADV_COLLAPSESynchronously collapse the range into transparent huge pages. Valid on another process.
any advice (self)Linux 6.13When pidfd refers to the calling process, any madvise advice is permitted.
flagsReserved; must be 0.

Errors you will actually hit

errnoMeaning
EPERMNo permission: needs PTRACE_MODE_READ_FSCREDS access to the target plus CAP_SYS_NICE (since Linux 5.12).
ESRCHThe target process has terminated and been waited on.
EBADFpidfd is not a valid PID file descriptor.
EINVALflags is not 0, n is too large, or the iov_len sum overflows ssize_t.
EFAULTiovec describes memory outside the target's accessible address space.

Example

int pidfd = pidfd_open(target_pid, 0);
if (pidfd == -1) err(1, "pidfd_open");

/* one 512 MB anonymous range, taken from /proc/<pid>/maps */
struct iovec range = {
    .iov_base = (void *)0x7f0000000000,
    .iov_len  = 512UL * 1024 * 1024,
};
ssize_t done = process_madvise(pidfd, &range, 1, MADV_PAGEOUT, 0);
if (done < 0) err(1, "process_madvise");
if ((size_t)done < range.iov_len)
    warnx("partial: %zd bytes advised", done);

Trimmed from the full walkthrough in process_madvise: memory pressure hints across process boundaries.

BSD equivalent

FreeBSD and OpenBSD: none; madvise(2) only works on the caller's own address space.

The long version, from the console

Sources

  1. process_madvise(2), man7.org
  2. madvise(2), man7.org
  3. glibc 2.36 NEWS (process_madvise added)

Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.