尽管 Azure Sphere OS 使用 Linux 内核作为基础,但请务必记住,你仍在为具有重要 RAM 约束的嵌入式设备编写应用程序。 应用良好的嵌入式编程实践将有助于创建可靠的 Azure Sphere 应用程序。
重要
若要获取应用程序的准确 RAM 使用情况信息,请务必 在不调试的情况下 运行应用。 在调试器下运行应用将导致 RAM 使用率过高,因为调试服务器使用的 RAM 将包含在报告的 RAM 使用情况统计信息中。 有关在附加设备上运行的应用程序的内存统计信息的详细信息,请参阅 高级应用程序中的内存使用。
下面是要遵循的一些最佳做法:
- 预先分配内存 (理想情况下静态) ,并尽可能在应用程序的生存期内分配内存。 这将大大提高应用程序的 RAM 使用率的确定性,并降低在应用程序生存期内内存占用增加和碎片的风险。
- 绝对需要动态分配时:
- 尝试将应用程序正在执行的堆内存分配和解除分配的频率降到最低,以降低堆内存碎片的风险,例如,利用区块分配/内存池技术。
- 查看堆栈页,如果可能,使用对 的调用
malloc()
memset()
包装调用,以强制提交页面。 这有助于确保如果分配导致应用程序超出其 RAM 限制,OS 将立即且可预测地终止它。 等待访问分配的页将引入延迟内存不足崩溃的风险,这更难重现和诊断。 - 在开发模式下启用 堆内存分配跟踪 。
- 避免与大字符串一起使用
Log_Debug
,并删除这些调用 (例如,#ifdef
) 不在开发模式下。Log_Debug
导致分配临时缓冲区,导致与大型字符串一起使用时 RAM 使用率突然突发。 - 尽可能对定期异步任务使用 EventLoop API, (例如) 与外围设备交互,而不是创建线程。 创建线程会导致 Linux 内核分配额外的内存,这些内存归于应用程序。 这会降低应用的确定性,因为它会增加 OS 计划程序在可能导致应用程序超出其 RAM 限制的多个不同操作之间切换的概率。 许多 Azure Sphere 示例应用程序(如 GPIO_HighLevelApp)演示了如何使用 EventLoop。
- 避免过早使用内存缓存来获取可在运行时重新计算的值。
- 使用 libcurl 时:
使用 libcurl 时优化最大套接字缓冲区大小。 Azure Sphere OS 将分配归因于应用程序的 RAM 使用情况的套接字缓冲区。 减少这些缓冲区大小是减少应用程序的 RAM 占用量的好方法。 请注意,使套接字缓冲区过小将对 libcurl 的性能产生不利影响。 相反,请优化方案的最大缓冲区大小:
static int sockopt_callback(void* clientp, curl_socket_t curlfd, curlsocktype purpose) { int size = /*specify max buffer sizes here (in bytes)*/ int size_size = sizeof(size); setsockopt(curlfd, SOL_SOCKET, SO_SNDBUF, &size, &size_size); setsockopt(curlfd, SOL_SOCKET, SO_RCVBUF, &size, &size_size); return CURL_SOCKOPT_OK; } // Place the following along with other calls to curl_easy_setopt curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, &sockopt_callback);
请参阅 CURLOPT_SOCKOPTFUNCTION libcurl 文档。
可以类似地调整更高级别 CURLOPT_BUFFERSIZE 和 CURLOPT_UPLOAD_BUFFERSIZE 参数。
Libcurl 还支持通过使用
curl_global_init_mem
和传入 、、free
、realloc
strdup
和calloc
的malloc
回调函数来重写其内部内存函数。 此功能使你能够跟踪动态分配,甚至更改行为。 例如,可以预先分配内存池,然后使用这些回调从该池分配 libcurl 内存。 这可以是设置防护措施和提高应用程序的确定性的有效方法。 有关如何使用这些回调的详细信息,请参阅 curl_global_init_mem libcurl 文档。注意
此回调机制并不涵盖由 libcurl 引起的所有内存分配,仅涵盖由 libcurl 本身直接进行的内存分配。 具体而言,不会跟踪由 wolfSSL 在下方进行的分配。