在 ESP32Arduino IDE上实现 mbedtls 和 xTaskCreate() 导致内存泄漏

问题描述

所以我有下面的代码。如果我直接在 loopTask 中调用 //next.config.js const rehypePrism = require("@mapBox/rehype-prism"); const withMDX = require("@next/mdx")({ extension: /\.mdx?$/,options: { rehypePlugins: [rehypePrism],},}); module.exports = withMDX({ pageExtensions: ["js","jsx","ts","tsx","md","mdx"],}); 函数,空闲堆是稳定的。但是,如果我使用该函数使用 signKey() 创建任务,空闲堆总是在减少。我错过了什么吗?

这是使用直接调用在串行监视器上的输出

xTaskCreate()

这是使用任务的串行监视器上的输出

FreeHeap: 358312
FreeHeap: 358312
FreeHeap: 358312
FreeHeap: 358312
FreeHeap: 358312
FreeHeap: 358312
FreeHeap: 358312
FreeHeap: 358312
FreeHeap: 358312
FreeHeap: 358312
FreeHeap: 358312
FreeHeap: 358312
FreeHeap: 358312
FreeHeap: 358312
FreeHeap: 358312
FreeHeap: 358312
FreeHeap: 358312
FreeHeap: 358312
FreeHeap: 358312
FreeHeap: 358312

这是代码

FreeHeap: 338364
FreeHeap: 337776
FreeHeap: 337228
FreeHeap: 336632
FreeHeap: 336080
FreeHeap: 335508
FreeHeap: 334960
FreeHeap: 334372
FreeHeap: 333812
FreeHeap: 333220
FreeHeap: 332672
FreeHeap: 332108
FreeHeap: 331552
FreeHeap: 330964
FreeHeap: 330416
FreeHeap: 329856
FreeHeap: 329304
FreeHeap: 328716
FreeHeap: 328152

更新: 我尝试在 #include "mbedtls/pk.h" #include "mbedtls/entropy.h" #include "mbedtls/ctr_drbg.h" static String privateKey = "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCID48Vp/9ydCMU\nzZ4n4iQ6pzr6eK+VCL7Lcpy8j/tBwLWk3jZ3ETmMm6Ua4MhUMc3ABN84uz0RgDi9\nb4RhOUy2SVbvfm5WZhlvpPmDD0yeTgWEyM3h3fBYPnRzMn/6KjsmCUgcfTWjdA+C\nj9vTL41j7bVCkU0/glBh5Rk3rKeImeX/lf4KfstguvJ+OlznCn039+tDlsCNrRgw\ni+L2AeI4UDZEDGWYRbONzU2WgJ11fr9J+Y6rSCwommDcxhdTtdlorZj2CFEtxRp5\nFCDm6Tvu0+aur8zpuhC+NFUWmYEHxztz18X1I4Pcy5cJpuqKL5t/Hxy5Yu7y32Ek\nE8DZv1DFAgMBAAECggEBAIBF+uW13tSuvSwttf9v6iwJ4UamZRKijg4MV6t9KqoQ\n3q8yeDLE4Ha5fmzaosMNuSZg8XnwvGA1fEjMTAfFF5d7iSR9E9UMqMpixIFU+Sz9\n7aIEFmXs8VygdPTuFU1qZx0y/vMs8FbLYpv6uIpfeHNPdeXuSt+nIdVJQf8FHWVg\nH7EJHJPCh0SoQZHQhV0M/n7K6iacXdC2k4AW2f2KtwPBOV19S+4Ymq7S6ycIyghd\nmMzMhWLgZYoPycMYQRDErEZOSHQHs+BqvKYqp/UVJNKAVihiqYXHmT8hnWMaaj8d\nQzVmcsq7dZO7vX+0Zfjd+krj9gbpCG30ESeNOx5sLAECgYEA0wDiGFyFAu6fVhm5\np4pHhGhFK765Ys3b5bapEgD1uN92BttPat2Qj11TKGdAERa8X0uuBB8McGcWUcvV\nFUIKYE0qj4k/oo7OEw5KmrQVlQLsWv4rls8XTEJcUnoq2lCTXzefuwEJFBkk4Bkb\nhioZE3CU0cV/Jg78vl37SJZbE4ECgYEApRNmRPCDWH8Jeo7OSqgee8Qy46R+JpBb\nmaaQ9dCO8pp5MYLQSA4C4BeHEpKq4v6C3HL7gf+Z6N/6/WU1X1bqDtXPdito5Zxc\nQ0Sa6URIPu7/txnu4Nc4GOfB6nG0/bxiqrDkxf7Kwk2E8xpmoCAQwDhg58Y1k61q\nO/iTjIJOj0UCgYEAo7yDtrPU47mYG5BK6R/871qakp+l7G4ivddIy5fDFnsRc7Cr\nqBnXG+knpqq4pIooEyr/FmOhm3fjcgXijGR6+M/ovwmaP+LhNxhX/ETSmpdyIgoq\neRSq15qHWdlDd7YfJPSA0vSyvs3kN6JEIZB5dQRf94hyam4m4vK7FFDYzAECgYEA\nlBiva7ILZF20d0ufL8NcddUzgp+UvaxNQa/55U7SsDx99jlR+xL26WyyNat3vGZx\nqK1PjvVtc0tete8SzxH+soiHs5CGb1i0PXVTNWuZFTz+FZU2VPFA1rc1dcvFgM59\np7osRKWt6lv5ptBMueOKo6jw538fmfm+kUcVuL0/FbECgYBx9ADNvyUaPq/3rQEa\n4Nzp+yyBL8r7iguLvI8EAYbCil9lACL+xXtScQ7mCY8EW8D8w/0cqA3Wjamb5ntS\n4T9Id1Iqq6MvzgJlTjNAYrsgoEC+fmU8iPnHNjxXrf74j4Mlh9pm5j6yeYsHQnMI\nv60FP7rcRiL2XnDJ5/ev/BWXaQ==\n-----END PRIVATE KEY-----"; static bool isTask = false; static bool printSerial = false; const char base64_chars[] = "ABCDEFGHIJKLMnopQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; static String base64_encode(const uint8_t *bytesToEncode,uint16_t len) { String ret; int i = 0; int j = 0; unsigned char char_array_3[3]; unsigned char char_array_4[4]; while (len--) { char_array_3[i++] = *(bytesToEncode++); if (i == 3) { char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for (i = 0; (i < 4); i++) { ret += base64_chars[char_array_4[i]]; } i = 0; } } if (i) { for (j = i; j < 3; j++) { char_array_3[j] = '\0'; } char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for (j = 0; (j < i + 1); j++) { ret += base64_chars[char_array_4[j]]; } } return ret; } static String base64_encode(String str) { return base64_encode((const unsigned char *) str.c_str(),str.length()); } uint8_t buffer[2000]; static void signKey(void *parameter) { String header = "{\"typ\":\"JWT\",\"alg\":\"RS256\"}"; String payload = "{\"payload\":\"my-payload\"}"; String content = base64_encode(header) + "." + base64_encode(payload); mbedtls_pk_context pk_context; mbedtls_pk_init(&pk_context); int rc = mbedtls_pk_parse_key(&pk_context,(uint8_t*)privateKey.c_str(),privateKey.length() + 1,NULL,0); if (rc != 0) { mbedtls_pk_free(&pk_context); if (printSerial)Serial.println("Failed to parse private key (err_code: 0x" + String(rc,HEX) + ")"); if (isTask)vTaskDelete(NULL); } mbedtls_entropy_context entropy; mbedtls_entropy_init(&entropy); mbedtls_ctr_drbg_context ctr_drbg; mbedtls_ctr_drbg_init(&ctr_drbg); const char* pers = "firebase-jwt"; mbedtls_ctr_drbg_seed(&ctr_drbg,mbedtls_entropy_func,&entropy,(const unsigned char*)pers,strlen(pers)); uint8_t digest[32]; rc = mbedtls_md(mbedtls_md_info_from_type(MbedTLS_MD_SHA256),(uint8_t*)content.c_str(),content.length(),digest); if (rc != 0) { mbedtls_pk_free(&pk_context); mbedtls_entropy_free(&entropy); mbedtls_ctr_drbg_free(&ctr_drbg); if (printSerial)Serial.println("Failed to digest content (err_code: 0x" + String(rc,HEX) + ")"); if (isTask)vTaskDelete(NULL); } size_t retSize; rc = mbedtls_pk_sign(&pk_context,MbedTLS_MD_SHA256,digest,sizeof(digest),buffer,&retSize,mbedtls_ctr_drbg_random,&ctr_drbg); if (rc != 0) { mbedtls_pk_free(&pk_context); mbedtls_entropy_free(&entropy); mbedtls_ctr_drbg_free(&ctr_drbg); if (printSerial)Serial.println("Failed to sign content (err_code: 0x" + String(rc,HEX) + ")"); if (isTask)vTaskDelete(NULL); } String signature = base64_encode(buffer,retSize); if (printSerial)Serial.println(signature); mbedtls_pk_free(&pk_context); mbedtls_entropy_free(&entropy); mbedtls_ctr_drbg_free(&ctr_drbg); if (isTask)vTaskDelete(NULL); } void tryTask() { isTask = true; xTaskCreate(signKey,"signKey",4096,1,NULL); } void tryDirectCall() { isTask = false; signKey(NULL); } void setup() { Serial.begin(115200); } void loop() { tryTask(); //tryDirectCall(); Serial.println("FreeHeap: " + String(ESP.getFreeHeap())); delay(5000); } 上创建任务并使任务不断循环,没有发生减少的空闲堆!所以这个问题只存在于我动态创建和删除任务时。为什么会发生这种情况?

解决方法

ESP32 有两种类型的内存——数据和指令。 Arduino 包装器返回 rather liberal interpretation 的空闲堆 - 查询的 cabability MALLOC_CAP_INTERNAL 可能包括这两种类型。所以可能只是代码缓存被一次性线程的垃圾填满了。

那个,或者你正在泄漏。在任何情况下,一次性线程都不是 micros 上常用的做法 - 它们使一切变得更加困难,包括此类分析。标准方法是在启动时创建一个永久的 FreeRTOS 任务,并在它必须执行某些操作时通过消息队列将工作项传递给它。我可以推荐 FreeRTOS tutorial 作为一个很好的资源。