Thirty years ago I would have told you /proc was the answer to everything. Open
/proc/<pid>/fdinfo/<fd>, read the pos: field, compare inode numbers from
/proc/<pid>/fd/<fd> symlinks. Done. Except it is not done, because between the
read in one process and the read in another, the first process can close and reopen
that fd three times. You are comparing stale data against fresh data and calling
it a fact.
kcmp(2) is the non-racy answer. It went into the kernel in Linux 3.5, designed
specifically for CRIU (Checkpoint/Restore In Userspace). It has been sitting there
since 2012 and most people writing tools that inspect processes do not know it
exists.
The interface
#include <linux/kcmp.h>
int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2);
The syscall has no glibc wrapper. Use syscall(SYS_kcmp, ...) directly.
pid1 and pid2 are the processes to compare. type says what to compare.
idx1 and idx2 are type-specific indices, usually file descriptor numbers.
The return value works like strcmp: negative if idx1’s object sorts before
idx2’s in kernel address order, zero if they are the same object, positive if
after. That ordering is not useful in isolation; you care about zero. But the
ordering exists for a real reason: CRIU needs to reconstruct sharing relationships
between hundreds of file descriptors across a frozen process tree. If you can sort
all the fd handles by their underlying object address, identical objects cluster
together and you do a single pass instead of an O(n²) equality check for each
pair. A clever design from people who needed it to work on production systems.
What you can compare
The type argument controls what the indices mean:
KCMP_FILE: idx1 and idx2 are file descriptor numbers in pid1 and pid2
respectively. Returns zero if they share the same open file description, meaning
the same struct file in the kernel. Two fds point to the same open file
description if one was duplicated from the other with dup(2), dup2(2), or
passed over a Unix socket with SCM_RIGHTS. An independent open(2) of the same
path produces two distinct file descriptions even if they refer to the same inode.
/* do pid1:fd3 and pid2:fd7 share the same open file description? */
int r = syscall(SYS_kcmp, pid1, pid2, KCMP_FILE, 3, 7);
/* r == 0: yes. r != 0: no, or an error. */
KCMP_VM: idx1 and idx2 are ignored. Returns zero if pid1 and pid2
share the same virtual memory space, that is, if one is a thread of the other
or if they were created with CLONE_VM without subsequent exec.
KCMP_FS: zero if they share the same filesystem context (root, cwd, umask).
Threads created with CLONE_FS share this; a forked process does not.
KCMP_FILES: zero if they share the same open file descriptor table entirely,
not just one fd. Threads created with CLONE_FILES share the table.
KCMP_SIGHAND: zero if they share the same signal handler table, as threads
created with CLONE_SIGHAND do.
KCMP_IO: zero if they share I/O context, the per-process accounting structure used by the block layer.
KCMP_SYSVSEM: zero if they share the same list of System V semaphore undo operations.
KCMP_EPOLL_TFD: idx1 is an epoll fd in pid1, idx2 is a target fd in
pid2. Returns zero if idx2 is registered with the epoll instance idx1.
This one is specifically for reconstructing epoll state during checkpoint.
Access requirements
You need ptrace access to both processes. In practice that means either:
- You are the same user as both target processes and
ptrace_scopeis not locked down to 0 (no ptrace). - You have
CAP_SYS_PTRACE.
If ptrace_scope is 1 (the Yama default on most Linux systems since about 2012),
you can only ptrace a process you spawned, not an arbitrary peer process. Check
/proc/sys/kernel/yama/ptrace_scope. Containers and checkpoint tools typically
run with the necessary privilege; a random daemon does not.
Why not just read /proc
Besides the race condition, /proc gives you inode numbers. The same inode
number in two different processes could mean they opened the same path but have
independent file descriptions with independent offsets, locks, and O_CLOEXEC bits.
Two fds that share a file description must track each other: a write through one
advances the offset seen by the other. That is a fundamentally different
relationship and /proc cannot tell you which you have without racy gymnastics.
KCMP_FILE returning zero is the only reliable answer to “is this fd the same
kernel object across process boundaries.”
Practical uses
Container checkpoint and restore. CRIU freezes a running process tree, dumps
its state to disk, and reconstructs it from scratch either on the same host or a
different one. Part of that reconstruction is getting fd sharing exactly right:
a thread that shares CLONE_FILES with another thread must end up sharing the
same fd table after restore, not two independent copies. kcmp makes that
determination without racing against a live process.
Debuggers and introspection tools. If you are building a tool that needs to
understand the fd topology of a running service, kcmp with KCMP_FILE across
all visible processes lets you build a precise sharing graph. Useful for leak
detectors and for verifying that SCM_RIGHTS-passed fds are actually the same
objects on both ends.
Runtime verification. If your IPC protocol depends on two cooperating
processes sharing a specific file description (a pipe, a memfd, a socket pair),
kcmp gives you a pre-flight check in tests without modifying either process.
What it cannot do
kcmp identifies kernel objects by their in-memory addresses, shuffled through
a per-boot hash to avoid leaking kernel ASLR. The ordering is not stable across
reboots or across exec. Do not serialize the non-zero return values; only the
zero/non-zero result has meaning outside a single kernel instance.
It also does not tell you the type of the object, what it refers to, or whether
it is still valid. You still need /proc for that; you just do not use /proc
to answer the identity question.
Availability
Linux 3.5+, released July 2012. No FreeBSD equivalent: the jail and ptrace
infrastructure gives you other ways in, but there is no kcmp. No macOS
equivalent either.
The pidfd series covers the broader fd-as-stable-identity
pattern: pidfd_open for process references, pidfd_getfd for fd theft across
process boundaries, and SCM_RIGHTS for explicit fd passing over Unix sockets.
If you are looking at this from the namespace angle, the
namespaces and containers tip
covers how processes end up sharing or not sharing the resources kcmp lets you
compare.
