The /proc directory looks familiar enough that it is easy to form the wrong mental picture: start a program, and Linux appears to create a folder of files for it. What you are really seeing is procfs—a kernel interface that renders process state through file operations. Once that clicks, entries such as exe, fd, maps, and status stop feeling mysterious.
Build a process that waits without burning a CPU core
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
printf("PID: %ld, argument: %s\n", (long)getpid(),
argc > 1 ? argv[1] : "none");
fflush(stdout);
for (;;)
pause();
}Why this process is friendlier than while(1)
getpid()returns the caller’s process ID in its current PID namespace.argcandargvpreserve the command-line arguments that process startup supplied tomain.fflush(stdout)makes the identifying line visible before the process blocks.pause()sleeps until a signal is delivered instead of continuously consuming a CPU.The default action for
SIGTERMends this program, which gives the cleanup step a conventional path.
gcc -std=c17 -Wall -Wextra -Wpedantic proc-demo.c -o proc-demo
./proc-demo "hello procfs" &
demo_pid=$!
printf 'shell captured PID: %s\n' "$demo_pid"
ps -p "$demo_pid" -o pid,ppid,state,comm,argsPID: 24173, argument: hello procfs
shell captured PID: 24173
PID PPID S COMMAND COMMAND
24173 ... S proc-demo ./proc-demo hello procfsCapture identity at process creation
&starts the command asynchronously in this shell.$!expands to the PID of the shell’s most recent asynchronous pipeline, avoiding an unreliableps | grepsearch.Quoting
$demo_pidprevents accidental word splitting and makes every later target explicit.The
Sstate commonly means interruptible sleep, which is expected whilepause()waits.Keep this terminal open because the shell variable is local to it.
/proc is a mounted kernel interface
findmnt -T /proc/$demo_pid
stat -f -c 'filesystem: %T' /proc/$demo_pid
ls -ld /proc/$demo_pidTARGET SOURCE FSTYPE OPTIONS
/proc proc proc ...
filesystem: procWhat these checks establish
findmnt -Tfinds the filesystem containing the target path.The filesystem type is
proc, not the disk filesystem that contains the executable.Directory ownership and mode expose some access rules, but individual proc entries can enforce additional ptrace-style checks.
Containers may mount their own procfs view, so identical numeric PIDs can mean different processes across PID namespaces.
Read the command line correctly
tr '\0' ' ' < /proc/$demo_pid/cmdline
printf '\n'./proc-demo hello procfsWhy cat can produce a misleading display
cmdlinestores argument strings separated by NUL bytes rather than newline-delimited text.trmakes those separators visible as spaces for this demonstration.A process can modify its argument strings, so the file is best understood as the command line the process currently presents.
A zombie has an empty
cmdline, and access may be denied for another user’s process.Command-line arguments are observable and should not carry secrets such as passwords or tokens.
Follow exe, cwd, and root symlinks
readlink -v /proc/$demo_pid/exe
readlink -v /proc/$demo_pid/cwd
readlink -v /proc/$demo_pid/root/home/user/demo/proc-demo
/home/user/demo
/Three paths describe different process context
exerefers to the executed file; it may show a(deleted)suffix if the pathname was unlinked while the process kept running.cwdis the process working directory used to resolve relative paths.rootis the process filesystem root and can differ because ofchroot, mount namespaces, or containers.Reading these links for another process is permission-controlled and can race with process exit or directory changes.
Inspect open file descriptors
ls -l /proc/$demo_pid/fd
for fd in 0 1 2; do
printf 'fd %s -> ' "$fd"
readlink "/proc/$demo_pid/fd/$fd"
done0 -> /dev/pts/3
1 -> /dev/pts/3
2 -> /dev/pts/3Descriptors are capabilities held by the process
Entries are symlinks named by descriptor number, not copies of file contents.
Descriptors 0, 1, and 2 conventionally represent standard input, output, and error.
Targets can be regular files, terminals, pipes, sockets, anonymous inodes, or deleted files.
The set can change between listing and reading it, so monitoring software must tolerate races.
Opening some
/proc/<pid>/fdlinks can duplicate access to the underlying object when permissions allow.
Connect virtual memory mappings to files
head -20 /proc/$demo_pid/maps... r--p ... /home/user/demo/proc-demo
... r-xp ... /home/user/demo/proc-demo
... r-xp ... /usr/lib/.../libc.so.6
... rw-p ... [heap]
... rw-p ... [stack]How to read one maps row
The address range belongs to the process virtual address space, not a physical RAM address.
Permissions use
r,w, andx, followed by private copy-on-writepor shareds.Offset, device, and inode can correlate a file-backed mapping with its source file.
Anonymous regions may be labelled
[heap],[stack], or have no pathname.ASLR means addresses normally change between executions; do not hardcode them.
Use status and stat for structured process facts
grep -E '^(Name|State|Pid|PPid|Threads|VmRSS|voluntary_ctxt_switches):' /proc/$demo_pid/status
cat /proc/$demo_pid/commName: proc-demo
State: S (sleeping)
Pid: 24173
PPid: ...
Threads: 1
VmRSS: ... kB
proc-demoPick an interface that matches the job
statusis human-readable and exposes identifiers, credentials, memory counters, capabilities, signals, and context-switch information.statis compact and machine-oriented, but its field parsing has traps such as a parenthesized command name that can contain spaces.commis a short process name and is not necessarily the executable basename or full command line.Memory counters are snapshots with kernel-defined accounting semantics, not immutable billing totals.
Namespaces change what “the PID” means
ls -l /proc/$demo_pid/ns
grep '^NSpid:' /proc/$demo_pid/status 2>/dev/null || truepid -> pid:[4026531836]
mnt -> mnt:[4026531841]
...
NSpid: 24173The process view depends on the observer
Namespace links identify the process membership for PID, mount, network, user, and other isolated resources.
NSpidcan show nested PID values from the procfs mount’s namespace toward the process namespace.A container’s
/proccan hide host processes and present PID 1 for a process that has another host PID.Do not combine a PID observed in one namespace with
/procmounted for another and assume the identity matches.
Stop the demonstration and verify cleanup
kill -TERM "$demo_pid"
wait "$demo_pid"
exit_status=$?
printf 'wait status: %d\n' "$exit_status"
test ! -e "/proc/$demo_pid" && echo 'proc entry is gone'wait status: 143
proc entry is goneCleanup closes the observation window
kill -TERMrequests termination of the exact captured PID; it is not a recursive or system-wide command.waitlets the parent shell reap its child and returns signal-derived status on common shells.Once the task exits, its procfs directory disappears.
The kernel can later reuse the number, so PID alone is not a permanent identity.
Status 143 conventionally means 128 plus signal 15 in the shell, but portable programs should reason about wait-status APIs rather than assume shell encoding.
Troubleshoot surprising observations
No `/proc/<pid>` directory: the process already exited, the PID was copied incorrectly, or it is outside your PID namespace.
Permission denied: ownership, ptrace access checks, capabilities, Yama, or procfs
hidepidpolicy restrict the entry.`cmdline` has no spaces: arguments are NUL-separated; render them deliberately.
`exe` says deleted: the running image still exists through kernel references even though its pathname was unlinked.
`maps` changes while reading: mappings are live state and other threads can call
mmap,munmap, or load libraries.PID points to the wrong program later: the original exited and its numeric identifier was reused.
Primary Linux references
Linux kernel procfs documentation describes process-specific directories, access controls, namespaces, and procfs behavior.
proc_pid manual indexes the process-specific proc entries.
proc_pid_cmdline manual documents NUL-separated arguments and process-controlled presentation.
proc_pid_maps manual defines mapping fields, permissions, and ptrace checks.
Comments and corrections