nx_proc_snapshot.nx
buildroot/runtime/nx_proc_snapshot.nx
about
nx_proc_snapshot.nx -- ONE /proc walk answers EVERY liveness question (seq1318 root fix).
THE DEFECT (measured 2026-07-30, source-grounded): nx_hostctl's proc_alive_by_name getdents-walks
all of /proc and reads every /proc/<pid>/cmdline -- and it runs ONCE PER GUARDED SERVICE PER POLL.
At ~25 guards x 951 processes that is ~23,775 cmdline reads per 15s poll (~1,585/s, ~4,750
syscalls/s) for LIVENESS ALONE, and it is a POSITIVE FEEDBACK LOOP: a fork storm inflates /proc,
which inflates the scan, which inflates kernel time. Measured context-switch rate 81k-93k/s.
THE FIX IS STRUCTURAL, NOT A TUNING KNOB: walk /proc ONCE, concatenate every cmdline into one blob,
and answer all ~25 questions from memory with ZERO further syscalls. O(P) per poll, not O(S x P).
A second, free property matters as much as the speed: every guard now reads the SAME INSTANT, so
two guards can no longer disagree because they scanned at different times.
THREE PROPERTIES MAKE IT SAFE BY CONSTRUCTION (not by caller discipline):
1. AGE-BOUNDED. ps_alive rebuilds when the snapshot is older than PS_MAX_AGE_MS. No call site has
to remember to refresh; an answer can never be unboundedly stale even if a caller forgets.
2. INVALIDATED ON MUTATION. ps_invalidate() after any spawn/kill forecloses the one DANGEROUS
staleness direction (snapshot says DEAD for a daemon that was just started -> a second spawn ->
EADDRINUSE churn). Stale-says-ALIVE merely defers a restart one poll, which is benign.
3. FAIL-SAFE, NEVER FAIL-WRONG. Open failure or blob overflow returns PS_UNKNOWN, and the caller
falls back to ps_alive_direct -- the exact pre-existing scan. Worst case is today's cost; a
wrong answer is impossible. (A silent cap that answered "not alive" would kill live daemons.)
ps_alive_direct is ALSO the gate's ORACLE: nx_proc_snapshot_gate proves snapshot == direct for every
probed name, so the fast path is verified against the slow one rather than asserted.
license_tier: ORIGINAL No hw writes (Rule 26). Importable (no main).
dependencies 1 imports · 2 importers
imports: nx_syscalls.nx
imported by: nx_hostctl.nxnx_proc_snapshot_gate.nx
structs
| none |
consts
| 29 | const PS_BLOBCAP: i64 = 4194304 // 4 MiB of cmdlines (~951 procs x ~100B measured = ~100KB; 40x headroom) |
| 30 | const PS_DENTBUF: i64 = 65536 // getdents64 batch |
| 31 | const PS_CLBUF: i64 = 8192 // per-pid cmdline read buffer (reused; never grows) |
| 32 | const PS_PATHBUF: i64 = 256 |
| 33 | const PS_STATEN: i64 = 64 // state cells |
| 34 | const PS_SEP: i64 = 1 // 0x01 record separator: not a legal byte of a process-name needle, |
| 36 | const PS_MAX_AGE_MS: i64 = 5000 // rebuild if older (safety net; the poll refreshes explicitly) |
| 37 | const PS_CONFIRM_AGE_MS: i64 = 250 // a NEGATIVE answer is only trusted from a snapshot this fresh |
| 38 | const PS_UNKNOWN: i64 = 0 - 1 // "I cannot answer" -> caller MUST fall back (never guessed) |
| 39 | const PS_ZERO: i64 = 48 |
| 40 | const PS_NINE: i64 = 57 |
functions
| 46 | func ps_init() -> i64 |
| 54 | func ps_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 56 | func ps_contains(hay: *u8, hn: i64, needle: *u8, nn: i64) -> i64 |
| 71 | func ps_read_cmdline(path: *u8, buf: *u8, cap: i64) -> i64 |
| 86 | func ps_build() -> i64 called by 2: ps_refreshps_fresh calls 10: ps_initsys_openat_rdsys_mmapsys_getdents64dirent_reclendirent_name+4 |
| 149 | func ps_invalidate() -> i64 { ps_init(); ps_st[3] = 0; return 0 } |
| 151 | func ps_refresh() -> i64 { return ps_build() } |
| 152 | func ps_valid() -> i64 { ps_init(); return ps_st[3] } |
| 153 | func ps_count() -> i64 { ps_init(); return ps_st[2] } |
| 154 | func ps_builds() -> i64 { ps_init(); return ps_st[4] } // build counter = the mechanical proof of the O(P) property |
| 155 | func ps_blob_len() -> i64 { ps_init(); return ps_st[0] } |
| 156 | func ps_age_ms() -> i64 |
| 162 | func ps_fresh(max_age_ms: i64) -> i64 |
| 174 | func ps_alive_direct(needle: *u8) -> i64 called by 2: ps_alive_or_directmain calls 10: ps_slensys_openat_rdsys_mmapsys_getdents64dirent_reclendirent_name+4 |
| 220 | func ps_alive_snap(needle: *u8) -> i64 |
| 240 | func ps_alive_or_direct(needle: *u8) -> i64 |