mlockall on Embedded Linux: Budget Future Heap and Stack Growth Before Enabling MCL_FUTURE
Enabling mlockall(MCL_CURRENT | MCL_FUTURE) can make a later allocation fail even when an embedded device still has free RAM. Future mappings must fit the process’s applicable locked-memory allowance. A successful startup call is not a reservation for every heap extension, thread stack or file mapping the service may create afterward.
This guide uses the Linux man-pages 6.19 mlock(2) page dated 2026-02-11. It considers a hypothetical acquisition service that wants to reduce paging-related delays. No memory limits, capabilities or scheduling settings were changed, and no exhaustion test was run.
Record what the flags commit to
The manual distinguishes currently mapped pages from future mappings. MCL_CURRENT covers the former; MCL_FUTURE extends locking to later mappings, including heap and stack growth. Locked-memory operations are page-granular.
The allowance is a separate resource boundary from free physical memory. Inspect the service’s actual RLIMIT_MEMLOCK soft limit and relevant capability state rather than assuming that an interactive shell has the same limits. The manual describes the privilege exception associated with CAP_IPC_LOCK; this article does not recommend granting it to bypass an unexplained allocation failure.
For an embedded deployment, capture those properties from the normal service launch. A developer’s successful shell demo does not establish the memory contract of the image’s supervised acquisition process.
Budget the complete growth path
Suppose a fictional service has an 8 MiB applicable locking budget and a reviewed 6 MiB current charge. A later feature introduces 1.5 MiB of additional mapped buffers and up to 1 MiB of additional mapped stack demand. The simplified total is 6 + 1.5 + 1 = 8.5 MiB, exceeding that budget.
These numbers are accounting inputs for a worksheet, not measured RSS or a prediction of precisely which allocation fails. Real accounting depends on page rounding, mapping layout, allocator behavior and the process’s actual policy. A heap allocation can reuse existing storage or require new mappings; those are different growth events.

Original hypothetical budget. Existing mappings and future growth must be reviewed together; free-RAM readings do not replace the locking allowance.
Include initialization that occurs only after the first client arrives: decoder tables, plugin loading, larger acquisition batches and additional worker threads. A service that survives an idle startup can still cross its resource boundary during the first uncommon request.
Prefer a phase-by-phase allocation inventory over a single “application buffer size.” Identify each mapping owner, its maximum supported extent and whether it can grow during the timing-sensitive phase. Record uncertain library behavior explicitly rather than assigning it a convenient zero.
Treat stack growth as a different failure mode
The manual warns that future-locking limits can make later mapping or allocation operations fail. If stack expansion cannot be accommodated, the kernel can deny growth and deliver SIGSEGV. That is not equivalent to a normal heap-allocation function returning an error that the application can gracefully handle.
Review the supported maximum call depth and per-thread stack use before entering the critical phase. Establish and touch the required working memory during a controlled initialization phase, within the reviewed budget. Do not demonstrate the boundary by recursively exhausting the stack of a running instrument.
Thread creation and unusual error paths deserve particular attention. Logging an elaborate diagnostic after an allocation failure can itself require more memory or stack. Keep the failure-reporting path compatible with the resources that remain available.
Do not confuse on-fault locking with prefaulting
MCL_ONFAULT changes when nonresident pages become populated and locked. It must accompany MCL_CURRENT, MCL_FUTURE or both. It does not promise that every page has already been touched, and it does not remove the applicable resource limits.
Consequently, adding it to make startup lighter is a different tradeoff from proving that the critical working set will not fault later. Decide which pages must be resident before acquisition begins and qualify that specific path. Memory locking alone does not establish hard real-time behavior or bound scheduler, driver and I/O delays.
Keep process lifecycle in the acceptance record
A later fork() can introduce copy-on-write faults; locking does not make that lifecycle harmless to latency. Avoid treating the service’s helper-launch path as unrelated to its memory design. Keep the exact startup and runtime process model in the review.
A proposed qualification record should contain the effective lock policy, checked return value, current mapping inventory, bounded future-growth estimate and behavior when an ordinary allocation cannot proceed. Separately record how stack demand was established without deliberate exhaustion.
Acceptance means the approved workload fits the reviewed memory contract and reports unsupported growth coherently. It is not established by one successful mlockall() call, one low RSS snapshot or a claim that the board has plenty of unused RAM.