proc-tree
Linux process tree — snapshot from /proc, incremental maintenance via fork/exec/exit events, ancestry chain queries, PID reuse detection, and pstree-style display.
Installation
Minimum supported Rust version: 1.85 (edition 2024).
About this crate
A Linux process tree library with a unified storage interface.
Key design decisions:
- Process tree only contains living processes — Exit removes the node, children are orphaned to init (PID 1)
- Unified storage — single
ProcessStoretrait for both tree structure and process info - O(1) child lookups —
children_indexmaintained on insert/remove
Quick Start
One-shot snapshot
use ;
let store = new; // TTL in seconds
snapshot;
// Resolve any PID
let info = resolve.unwrap;
println!;
// Render pstree-style tree
println!;
Incremental maintenance
use ;
let store = new;
snapshot;
// Events from proc-connector, audit, or any source
let exited = handle_events;
// Caller explicitly removes when done processing related events
for ep in exited
Complete Example
use ;
// Create store (TTL in seconds)
let store = new;
// Seed from /proc
snapshot;
// Resolve a PID
let info = resolve.unwrap;
println!;
// Build ancestry chain: "200|bash|root;100|sshd|root;1|systemd|root"
let chain = build_chain_string;
// Query relationships (O(1) for children)
let kids = children; // direct children of PID 1
let all = descendants; // all descendants (BFS)
let sibs = siblings; // same-parent processes
// Find by name or user (O(n) - requires full scan)
let sshds = find_by_cmd;
let roots = find_by_user;
// Ancestry check
let mine = id;
if is_descendant
// pstree-style display
println!;
Types
ProcessInfo
ProcEvent
| Variant | Behavior |
|---|---|
Fork |
Inserts a new process (child_pid → parent_pid), cmd left empty |
Exec |
Reads /proc/{pid}/status to update cmd, user, ppid, tgid |
Exit |
Returns ExitedProcess handle, orphans children to init (PID 1) |
ProcessLink
Displayed as "pid|cmd|user". A chain is a Vec<ProcessLink> ordered from child to ancestor.
ExitedProcess
Returned by handle_event / handle_events for Exit events. The process info stays in the store until remove() is called, allowing late-arriving events to still look up process info.
let exited = handle_event.unwrap;
assert!; // still accessible
exited.remove; // explicitly remove when done
assert!;
Trait (custom backend)
Implement ProcessStore for any storage (Redis, moka, dashmap, ...):
All functions in ops are generic over this trait — bring your own storage.
Performance note:
for_each_childis the core method — it iterates children without allocating a returnVec. Thechildren_ofconvenience method has a default implementation that collects into aVec. Hot paths (e.g.,handle_eventExit handler) usefor_each_childdirectly for zero-allocation iteration.
Performance
| Operation | Complexity | Notes |
|---|---|---|
children(pid) |
O(1) | Uses children_index |
descendants(pid) |
O(k) | k = number of descendants |
build_chain_links(pid) |
O(d) | d = depth of process |
is_descendant(pid, cmd) |
O(d) | d = depth of process |
find_by_cmd(cmd) |
O(n) | n = total processes |
find_by_user(user) |
O(n) | n = total processes |
snapshot() |
O(n) | n = total processes |
PID Reuse Detection
When a process exits and its PID is reused, cached data becomes stale. The start_time_ns field (nanoseconds since boot, from /proc/{pid}/stat) lets implementations detect reuse by comparing cached vs. current values.
Thread Safety
DefaultStore is Arc<Mutex<HashMap>> — clone shares the same underlying data. Safe to pass across threads.