Read a Linux Process's Wait Channel with /proc/PID/wchan
You will finish with a small, repeatable way to see the kernel function where a sleeping Linux process is blocked. The result is a clue for triage, not a complete explanation of why the process is slow or stuck.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about ten minutes. You need a shell and access to /proc. The examples were checked on Linux kernel 6.8.0-139-generic with manpages 6.7-2. Reading your own processes normally needs no elevated privileges. Do not start by running the whole investigation as root: the relevant permission check is tied to ptrace access, and changing identity can hide the access problem you need to understand.
1. Confirm the file and its meaning
/proc/PID/wchan is a virtual file. Replace PID with a decimal process ID and read it as text:
$ printf 'PID=%s\n' 12345
PID=12345
$ cat /proc/12345/wchan
futex_wait_queue
The name is the symbolic kernel location corresponding to the place where the process is sleeping. It is not the process name, a signal state, or a persistent label. The value can change as the process runs, so read it at the time you are investigating the behaviour.
Checkpoint: verify that the target still exists before interpreting the value:
$ test -r /proc/12345/wchan && cat /proc/12345/wchan || printf 'process is gone or wchan is not readable\n'
If the process exits between the two operations, the file can disappear. That is ordinary process lifetime behaviour, not evidence that the kernel lost the process.
2. Create a harmless sleeping process
Use sleep to practise without touching a service or changing system state. This is an ordinary user command:
$ sleep 30 &
[1] 206756
$ target_pid=$!
$ printf 'target PID: %s\n' "$target_pid"
target PID: 206756
$ cat "/proc/$target_pid/wchan"
hrtimer_nanosleep
Your PID and wait-channel name can differ. On the checked host, sleep 30 was in hrtimer_nanosleep. That is consistent with a process waiting for its timer, and does not mean that every process sleeping for a duration will show precisely that spelling on every kernel or libc combination.
When you are done with the test, stop it and collect its status:
$ kill "$target_pid"
$ wait "$target_pid" 2>/dev/null || true
$ test ! -e "/proc/$target_pid" && echo 'test process has exited'
test process has exited
The kill command changes the test process state, but it does not modify persistent configuration. Never substitute a production service PID in this cleanup step.
3. Correlate the channel with process state
A wait-channel name is most useful beside the process state and command name. ps can read the same information for a quick snapshot:
$ sleep 30 &
$ target_pid=$!
$ ps -o pid=,ppid=,stat=,comm=,wchan= -p "$target_pid"
207001 204900 S sleep hrtimer_nanosleep
$ kill "$target_pid"
$ wait "$target_pid" 2>/dev/null || true
The exact spacing, parent PID and channel vary. In this example, S means the process is interruptible sleep according to the process-state column, while wchan gives a more specific kernel wait location. Treat the two columns as a snapshot: a runnable process may show a different channel by the time a second command reads it.
For a real target, capture a few samples rather than assuming that one read describes the whole incident:
$ target_pid=12345
$ for sample in 1 2 3 4 5; do
> date '+%H:%M:%S'
> ps -o pid=,stat=,comm=,wchan= -p "$target_pid"
> sleep 1
> done
If the process is no longer present, ps prints no process row and later reads may fail. Record that disappearance instead of replacing the PID with a newly created process that happens to have the same number.
4. Understand a zero or unhelpful value
The kernel's proc documentation describes wchan as the kernel function where the task is blocked, or 0 when it is not blocked. It also documents the dependency on a kernel built with CONFIG_KALLSYMS. A process that is running, waking, or between waits may therefore produce 0 or a value that is difficult to use.
Do not turn one name into a diagnosis. For example, a wait in a futex-related function may be normal synchronisation, or it may reflect a lock that another thread never releases. Read the process state, command line, logs and application-level metrics before deciding that a kernel wait is the fault.
Do not rely on the exact function name in scripts. Kernel configuration, architecture, symbol availability and kernel changes can alter the text. If automation needs a stable interface, use an interface designed for that purpose rather than matching arbitrary wchan output.
5. Handle permission failures safely
The installed proc_pid_wchan(5) manual says access is governed by a PTRACE_MODE_READ_FSCREDS check. A read of another user's process, a process in a restricted environment, or a process hidden by system policy can fail even when the PID exists.
$ target_pid=12345
$ if value=$(cat "/proc/$target_pid/wchan" 2>/tmp/wchan-error); then
> printf 'wchan: %s\n' "$value"
> else
> printf 'could not read /proc/%s/wchan: ' "$target_pid"
> cat /tmp/wchan-error
> fi
This example writes a temporary diagnostic under /tmp. Remove it after inspection if it contains information you do not need:
$ rm -f /tmp/wchan-error
Before using elevated privileges, confirm the target PID, owner and command:
$ ps -o user=,pid=,comm=,args= -p "$target_pid"
Only use an approved operational method to inspect a process you are authorised to inspect. sudo cat may change the credentials used for the read, but it does not make the channel more accurate and does not repair a missing process.
6. Keep the result in proportion
wchan answers a narrow question: where does the kernel say this task is sleeping at the instant of the read? It does not show the complete kernel stack, identify the thread that owns a lock, prove that a service is unhealthy, or explain what the application intended to do.
Use it as one line in a time-stamped investigation. Pair it with ps state and command details, service logs, and application-specific diagnostics. If you need a deeper kernel investigation, choose a tracing or profiling tool appropriate to the host's change-control rules. Reading wchan itself is read-only and does not require stopping or restarting the target.
Done means
- You can read
/proc/PID/wchanfor a confirmed, still-running PID. - You understand that the value is a changing snapshot of a sleeping kernel location.
- You correlated it with
psstate instead of treating the name as a diagnosis. - You know that
0, a missing file or a permission error each has a different meaning. - You tested with a temporary
sleepprocess and cleaned it up. - You did not change a service, persistent configuration or security policy.