在高级应用程序中管理 RAM 使用情况的最佳做法

重要

这是 Azure Sphere(旧版)文档。 Azure Sphere(旧版)将于 2027 年 9 月 27 日停用,用户此时必须迁移到 Azure Sphere(集成)。 使用位于 TOC 上方的版本选择器查看 Azure Sphere(集成)文档。

尽管 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_BUFFERSIZECURLOPT_UPLOAD_BUFFERSIZE 参数。

      • Libcurl 还支持通过使用curl_global_init_mem和传入回调函数来替代其内部内存函数,例如mallocfreereallocstrdupcalloc。 此功能使你能够跟踪动态分配,甚至更改行为。 例如,可以提前分配内存池,然后使用这些回调从该池分配 libcurl 内存。 这可以是设置防护措施和增加应用程序的确定性的有效技术。 有关如何使用这些回调的详细信息,请参阅 curl_global_init_mem libcurl 文档。

        注意

        此回调机制不包括 libcurl 导致的所有内存分配,仅涵盖 libcurl 本身直接进行的内存分配。 具体而言,下面狼SSL进行的分配没有跟踪。