A supervisor records PID 1842 for a worker, waits for an asynchronous event, then sends a signal. Between those operations the worker can exit, be reaped, and its numeric PID can later identify another process. The number still looks valid, but the identity it denotes has changed.
Linux pidfds move this class of process control away from repeated numeric lookup. A PID file descriptor refers to a particular process, giving userspace a kernel-held reference that can be passed to interfaces such as pidfd_send_signal(), polling APIs, and, under additional permission checks, pidfd_getfd().
Numeric PIDs are names, not durable handles
A PID identifies a task within a PID namespace. Its usefulness as an identifier does not make it permanent. After a process exits and the relevant lifecycle conditions permit reuse, the kernel can assign the same numeric value to a later process.
That creates a time-of-check to time-of-use problem when software stores a PID, verifies some property, and later performs another operation by PID. The second operation resolves the number again. If lifecycle state changed in the interval, the lookup can target a different process.
Extra checks against /proc/<pid> can narrow some application mistakes but do not turn a numeric PID into an object reference. The kernel documentation also notes that an already-open descriptor for /proc/<pid> does not begin operating on a later process if the number is reused; operations tied to the dead process fail instead. Pidfds provide a dedicated process-reference interface for this model.
pidfd_open binds the descriptor to one process
pidfd_open(pid, flags) obtains a file descriptor referring to the process selected by the supplied PID. Linux introduced the system call in 5.3. The returned descriptor has close-on-exec set, so a successful exec does not propagate it by default.
The important property is identity stability after acquisition. The pidfd does not become a reference to a replacement process merely because the numeric PID is recycled. Operations accepting the pidfd use the referenced process rather than performing a fresh selection from a stored PID number.
Acquisition still has a boundary. pidfd_open() begins with a numeric PID, so the caller must account for lifecycle changes before that call completes. For a newly created child, Linux offers CLONE_PIDFD through process-creation interfaces, allowing creation and pidfd acquisition to be coupled. The pidfd_open() documentation also describes conditions under which opening a pidfd for a forked child remains safe before that child is reaped.
Signaling gains identity stability, not extra privilege
pidfd_send_signal() sends a signal to the process referenced by a pidfd. Its security value is precise: it avoids the target-substitution race associated with signaling solely by a PID that may have been recycled.
A pidfd is not a permission bypass. Signal permission checks still apply. Possessing the descriptor establishes which process is being named; it does not automatically authorize every operation against that process.
If the referenced process has terminated, pidfd_send_signal() does not redirect the signal to a later process carrying the same PID. The operation fails with ESRCH once the referenced process is no longer a valid signal target.
This separation between stable naming and authorization is central to safe use. A service can eliminate PID-reuse ambiguity while still relying on Linux credentials and capability checks for the operation itself.
Polling turns lifecycle state into descriptor readiness
Pidfds can be monitored with poll(), select(), and epoll(). Process termination therefore fits the same event-loop machinery used for sockets, pipes, and other descriptors.
This removes a common architectural mismatch in supervisors. A numeric PID is passive data and requires a separate lifecycle mechanism. A pidfd is both an identity reference and an object that exposes termination state through descriptor readiness.
Readiness is not process output. Current Linux pidfds are not byte streams; read() does not return an exit record. Waiting and status collection remain governed by the relevant wait interfaces and process relationship. A pidfd referring to a child can be used with waitid() under the documented rules.
pidfd_getfd crosses a stronger authority boundary
pidfd_getfd() can duplicate a file descriptor from another process into the caller. This operation is materially different from merely observing process exit or selecting a signal target. It can transfer access to a kernel object already opened by the target.
Linux subjects this operation to ptrace-style access control. A pidfd therefore identifies the source process stably, while a separate authorization decision governs whether descriptor extraction is permitted.
That distinction prevents an overly broad interpretation of pidfds as process capabilities in the sense of unrestricted authority. Individual pidfd-based operations retain their own permission models. Stable identity is a foundation on which those checks act, not a replacement for them.
Namespace context still defines numeric acquisition
PID namespaces affect the numeric PID visible to a caller. A pidfd acquired from a visible PID refers to the selected process object, but the initial lookup still occurs in the caller’s applicable namespace context.
This matters when control software spans container or namespace boundaries. Passing around bare PID integers can silently mix identifier domains. Passing an already acquired pidfd preserves the referenced process across that handoff, subject to descriptor-transfer and operation-specific permission rules.
Pidfds therefore tighten one specific trust boundary: they separate process identity from the reuse semantics of numeric names. They do not freeze process state, grant universal control, or erase namespace and credential policy. Their security property is narrower and more useful: once a process reference has been acquired correctly, later pidfd operations do not retarget themselves to an unrelated process merely because a PID number has been recycled.