内存
这是 Mbed OS 中内存模型的基本概述。
+---------------------+ Last address of RAM
| Scheduler/ISR stack |
+---------------------+
| ^ |
| | |
| |
| Heap cont. |
|---------------------|
| User thread n stack |
|---------------------|
| User thread 2 stack |
|---------------------|
| User thread 1 stack |
|---------------------|
| ^ |
| | |
| |
| Heap |
+---------------------+
| |
| ZI: Global data |
| |
+---------------------+
| ZI: Idle stack |
+---------------------+
| ZI: Timer stack |
+---------------------+
| ZI: Main stack |
+---------------------+
| |
| ZI: Global data |
| |
+---------------------+
| RW: Vector table |
+=====================+ First address of RAM
| | Last address of flash
| |
| Application |
| |
| |
+---------------------+
| |
| Optional bootloader |
| |
+---------------------+
| RO: Vector table |
+---------------------+ First address of flash
系统中至少有两种内存:flash 和 RAM。
RAM
在 RAM 内部,您可以区分两种逻辑类型:静态和动态内存。静态内存在编译时分配,因此在运行时不会更改大小。动态内存在运行时分配。例如,当您分叉和连接线程以及构造和销毁对象时,程序内存使用会增长和缩小。系统以不同的方式使用它们中的每一个:
- 静态:
- 向量表(读和写)。
- 全局数据。
- 静态数据。
- 默认线程的栈(main, timer, idle 和 scheduler/ISR)。
- 动态:
- 堆(动态数据)。
- 用户线程的栈。Mbed OS 在堆上为用户线程的栈动态分配内存。
对所有线程打开堆栈检查,如果检测到溢出情况,则发生内核错误。
Flash
Flash 是一种只读存储器(ROM),包含:
- 向量表(只读)。
- 应用程序代码。
- 应用程序数据。
- 可选的引导程序。
静态内存优化
删除未使用的模块
对于像 Blinky 这样闪烁 LED 的程序这样的简单程序,典型的内存使用情况分为以下几个模块:
+---------------------+-------+-------+-------+
| Module | .text | .data | .bss |
+---------------------+-------+-------+-------+
| Fill | 132 | 4 | 2377 |
| Misc | 28807 | 2216 | 88 |
| features/frameworks | 4236 | 52 | 744 |
| hal/common | 2745 | 4 | 325 |
| hal/targets | 12172 | 12 | 200 |
| rtos/rtos | 119 | 4 | 0 |
| rtos/rtx | 5721 | 20 | 6786 |
| Subtotals | 53932 | 2312 | 10520 |
+---------------------+-------+-------+-------+
功能/框架模块包括 Mbed OS 测试工具,即使您不再测试您的程序。因此,您正在构建每个二进制文件中的一个测试工具。删除此模块可节省大量 RAM 和 flash。
Printf 和 UART
链接器还可以删除程序不使用的其他模块。例如,Blinky 的主程序不使用 printf 或 UART 驱动程序。但是,每个 Mbed OS 模块通过将其错误消息重定向到串行输出上的 printf 来处理跟踪和断言 - 强制打印 printf 和 UART 驱动程序并需要大量的闪存。
要禁用错误记录到串行输出,请在程序的 mbed_app.json 文件中设置 NDEBUG 宏和以下配置参数:
{
"macros": [
"NDEBUG=1"
],
"target_overrides": {
"*": {
"platform.stdio-flush-at-exit": false
}
}
}
注意: 不同的编译器,不同的结果;使用一个编译器进行编译会产生与使用另一个编译器编译不同的内存使用。
嵌入式目标
您还可以利用这些程序仅在嵌入式目标上运行的事实。在桌面计算机上运行 C++ 应用程序时,操作系统会在调用 main 之前构造每个全局 C++ 对象。它还会在程序结束时注册一个句柄来销毁这些对象。编译器注入的代码对应用程序有一些影响:
- 编译器注入的代码消耗内存。
- 它意味着动态内存分配,因此要求二进制文件包含 malloc,即使应用程序不使用它也是如此。
在嵌入式设备上运行应用程序时,程序退出时不需要处理程序来销毁对象,因为应用程序永远不会结束。通过在应用程序启动时删除析构程序注册,并在操作系统在运行时调用 exit() 时消除用于销毁对象的代码,可以节省更多 RAM 和 flash 使用量。
注意: 默认情况下,Mbed OS 5.2.0+ 可以删除析构程序注册。