$RodHat_
Syscall reference · Processes

pidfd_open(2)

Get a file descriptor that refers to a process, immune to PID reuse.

Since
Linux 5.3check your kernel →
glibc
glibc 2.36
System
Linux
Man page
pidfd_open(2)

Synopsis

#include <sys/syscall.h>
#include <unistd.h>

int syscall(SYS_pidfd_open, pid_t pid, unsigned int flags);

Rod's take

A PID is a number the kernel will hand to someone else the moment your child dies, and kill(2) has trusted that number since Edition 6. pidfd_open gives you a reference instead. Poll it, signal it, waitid(P_PIDFD) on it, and you're talking to the process you meant, not whatever inherited the slot. Fine print: open it after fork() and it's only race-free if nobody else reaped the child and SIGCHLD isn't ignored; otherwise use clone3 with CLONE_PIDFD. PIDs for logging, pidfds for control. Fifty years late, but correct.

Flags and arguments that matter

NameWhat it does
PIDFD_NONBLOCKLinux 5.10waitid(2) on the fd returns EAGAIN instead of blocking if the task hasn't terminated.
PIDFD_THREADLinux 6.9Refer to a specific thread rather than a thread-group leader.
poll/epollThe fd turns readable (EPOLLIN) when the process exits and becomes a zombie; EPOLLHUP once it is reaped.
waitid(P_PIDFD)If the pidfd refers to your child, wait on it by fd instead of by PID.

Errors you will actually hit

errnoMeaning
ESRCHThe process specified by pid does not exist.
EINVALflags is not valid, or pid is not valid.
EMFILEPer-process open file descriptor limit reached.
ENODEVThe anonymous inode filesystem is not available in this kernel.

Example

int pfd = syscall(SYS_pidfd_open, pid, 0);
if (pfd < 0)
    err(1, "pidfd_open");           /* ESRCH: already gone */

struct pollfd p = { .fd = pfd, .events = POLLIN };
poll(&p, 1, -1);                     /* readable once it exits */

siginfo_t info = {0};
waitid(P_PIDFD, pfd, &info, WEXITED); /* only if it's your child */
close(pfd);

Trimmed from the full walkthrough in pidfd: Unix finally has a handle on your processes (only took 50 years).

BSD equivalent

FreeBSD: process descriptors created at fork time with pdfork(2), plus pdgetpid(2) and EVFILT_PROCDESC in kqueue(2) (FreeBSD 9.0). OpenBSD: none.

The long version, from the console

Sources

  1. pidfd_open(2), man7.org
  2. pdfork(2), FreeBSD
  3. kqueue(2), FreeBSD
  4. glibc 2.36 NEWS (pidfd_open, pidfd_getfd, pidfd_send_signal added)

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