## 栈估算 ## 判别规则(核心,唯一要记的) 1. **估**:栈大小 = **最坏路径峰值(局部变量 + 调用帧 + 库函数 + 中断嵌套)× 余量系数(经验 1.5~2)**,宁大勿小 2. **验**:怀疑不够就用 `uxTaskGetStackHighWaterMark` 查**历史最低剩余**,留 **20%+** 余量;数字说话,别靠猜 一句话:**够用 + 验证,不是开大保平安,也不是开小赌运气。** ## 抽象描述(一句话本质) 任务栈 = 任务运行瞬间的"暂存区"(局部变量 + 调用帧 + 保存的寄存器)。估错 → 溢出踩坏相邻内存/相邻任务的栈 → 诡异跑飞、随机崩溃、难复现。 ## 正反例(建立直觉) - **正例1**:任务有 32 字节局部数组 + 调 3 层函数 + 用 printf → 峰值按局部+调用帧+库函数栈算,×1.5~2 → 128 words 起步 - **正例2**:用 `uxTaskGetStackHighWaterMark(&task)` 查历史最低水位 → 只读不写,跑几天看真实峰值再调 - **正例3**:ISR 里也有栈使用(中断嵌套把异常帧压进当前任务栈)→ 估算时计入最大中断嵌套 + ISR 局部变量 - **反例1**:栈只开到"编译能过"的 64 words → 某条异常路径(深递归/大临时数组)就溢出 → 踩进相邻任务/系统堆 - **反例2**:栈开 4096 words "保证永不溢出" → 浪费 RAM;嵌入式 RAM 金贵,栈/堆共用一块内存,此消彼长 - **反例3**:只手算估计从不验证、不留余量 → 现实峰值 ≠ 估计值,加个功能就溢出 ## 实际算例 任务局部变量 80 字节 + 调用帧 40 字节 + 中断嵌套最多 2 层 × 24 字节 = 峰值 168 字节 ≈ **42 words**。×2 = 84 words < 128 words → **128 words 够**。 HighWaterMark 判据:栈 128 words,返回 32 → 剩余 25%,**刚过 20% 线**——能过但贴着线,后续加功能/中断变深逼近线以下就该加大。 ## 易混对比 | | 栈(stack) | 堆(heap) | | -------- | -------------------------- | --------------------- | | 存放 | 局部变量/调用帧/保存寄存器 | malloc 出来的动态内存 | | 谁分配 | 编译器+运行时自动 | 程序员 malloc/free | | 溢出后果 | 踩坏相邻任务/内存 | 分配失败/碎片 | ## 变体验证(3 题,全过=学会) 1. 局部 80B + 调用帧 40B + 中断嵌套 2×24B,128 words 够吗?→ **够**(峰值 42 words,×2=84 < 128) 2. 栈 128 words,HighWaterMark 返回 32 → **不用加大**(32/128=25%,刚过 20% 线,但要盯着,接近 20% 就该加) 3. 陷阱题:栈开 4096 words 保证不溢出 → **不对**(资源消耗太高,栈/堆共用 RAM,此消彼长) ## 口述要点(面试怎么讲) - **结论先行**:栈 = 最坏路径峰值 × 余量;用 HighWaterMark 验证 - **为什么看历史最低剩余**:当前剩余随运行忽高忽低,只有历史最低才暴露任务这辈子最深的调用路径(最坏情况) - **栈方向与溢出后果**:栈向下增长,溢出覆盖相邻内存/相邻任务栈的寄存器值 → 程序跑飞、随机崩溃;FreeRTOS 有兜底:`configCHECK_FOR_STACK_OVERFLOW` + `vApplicationStackOverflowHook` 钩子,但检测有滞后性,不能完全依赖 - **为什么不能开大**:栈和堆在同一块 RAM,此消彼长;嵌入式资源紧张,正确姿势是"够用 + 验证" - **易错点**:估算漏掉中断嵌套现场;靠"编译能过"判断栈够用 ## 关系网络 | 相邻概念 | 关系 | 孤立理解会犯的错 | | ------------- | ---------------------------------------------- | ---------------------------------------- | | 内存分配方式 | 栈是三大内存区之一,与静态区/堆并列 | 只关心堆 malloc,忘了任务栈才是 RAM 大头 | | 程序分段 | 栈段运行时动态伸缩,向下增长 | 以为栈大小编译期固定 | | ISR 通信 | 中断嵌套的异常帧压在任务栈上 | 估算栈时漏算中断嵌套 → 一触发就溢出 | | 内存泄露/碎片 | 栈溢出会顺带踩坏堆结构,症状伪装成 malloc 失败 | 报错指向堆,根因是栈溢出 | ## 学习日期 2026-08-03 --- _每主题一页,复习时只翻本目录。_