在上报MQTT数据时,采用CJSON格式。由于每次上报数据都需执行CJSON格式转换,而此转换过程会涉及动态内存分配。如此一来,在上报MQTT消息时,便会导致内存频繁地进行动态申请与释放,进而产生内存碎片。
为解决这一问题,我自定义了静态内存分配方式,并将其与CJSON标准库相结合。通过这种方式,成功解决了频繁动态申请内存的难题,有效避免了内存碎片的产生 。
一般步骤是将CJSON对象使用cJSON_PrintUnformatted函数转换成普通字符串,然后封装到MQTT报文中,但是cJSON_PrintUnformatted函数中会多次调用malloc函数动态申请内存,如果每一次上报消息都要频繁申请内存,久而久之必定会产生大量内存碎片。
静态内存分配是一种不错的解决方案:
1.自定义内存分配函数
#include "cJSON.h"
#include "string.h"
static uint8_t mem_pool[2048];
static size_t mem_offset = 0;
void* custom_malloc(size_t size)
{
if(mem_offset + size > 2048)
return NULL;
void* ptr = &mem_pool[mem_offset];
mem_offset += size;
//printf("\r\n mem_offset: %d\r\n",mem_offset);
return ptr;
}
void custom_free(void* ptr)
{
//静态内存无需释放,不执行任何操作
//不能在该函数中修正offset偏移量,因为custom_malloc函数会在cJSON_PrintUnformatted中被连续多次调用
//,因此offset修正因该在最后一次custom_malloc函数调用完毕执行,即在执行完cJSON_PrintUnformatted函数后再修正偏移量mem_offset
}
2.将自定义的内存分配函数使用CJSON标准库的钩子函数注册
//使用自定义的静态内存分配方法,防止在转换CJSON字符串时出现内存碎片问题
cJSON_Hooks hooks = {.malloc_fn = custom_malloc,.free_fn = custom_free};
cJSON_InitHooks(&hooks);
3.定义偏移量修正函数
void reset_malloc(void)
{
mem_offset = 0;
}
4.转换CJSON字符串,并封装到MQTT报文中
xSemaphoreTake(xMutexPUBUF,portMAX_DELAY); //保护PUB_BUF缓存区
json_body = cJSON_PrintUnformatted(pOrder);
//json_body = cJSON_PrintBuffered(pOrder,1000,0);
memset(PUB_BUF,0,PUBLISH_MAX_SIZE);
memcpy(PUB_BUF,json_body,strlen(json_body));
//静态分配的内存不需要释放,但是需要修正数组游标的偏移量,
//当下一次转换字符串时,重新从静态数组第一位置开始存放数据
reset_malloc();
xSemaphoreGive(xMutexPUBUF);
5.在上报数据的任务中上报数据
xSemaphoreTake(xMutexPUBUF,portMAX_DELAY); //抢占锁
Mqtt_Publish(devPubTopic,PUB_BUF);
xSemaphoreGive(xMutexPUBUF); //释放锁资源
6.使用FreeRTOS中的内存查询函数检查内存使用情况
printf("xPortGetFreeHeapSize = %d\r\n",xPortGetFreeHeapSize());
printf("-X:run-B:block-R:ready-D:del-S:suspend-Heap mini %d-\r\n", xPortGetMinimumEverFreeHeapSize());
vTaskDelay(1000);