littlevgl内存管理

Littlevgl的内存管理其实还是有点粗糙的。总体来看,跟前面我写的文章中的rtos的内存管理方式差不多。它包括了碎片管理,不过并不完善。


简单说一下它的管理方式,然后贴上代码,一段段来分析。

littlevgl的内存管理,其实是将每一个内存块再细分:(头部+数据)+(头部+数据)+...=总内存。这样做就需要做到:头部有标志说明该块内存是否被占用以及该块内存的大小。所以,这样就讲完了littlevgl的内存管理了。

接着看代码:

内存的头部使用如下结构体表示:

<code>/*The size of this union must be 4 bytes (uint32_t)*/
typedef union
{
struct
{
MEM_UNIT used : 1; /* 1: if the entry is used*/
MEM_UNIT d_size : 31; /* Size off the data (1 means 4 bytes)*/
} s;
MEM_UNIT header; /* The header (used + d_size)*/
} lv_mem_header_t;/<code>

其中,属性header可以明确表示它这个union作用是什么,有见名知义的作用。


<code>typedef struct
{
lv_mem_header_t header;

uint8_t first_data; /*First data byte in the allocated data (Just for easily create a pointer)*/
} lv_mem_ent_t;/<code>

这个结构体则使用first_data来指定内存数据的起始地址,它定义了unit8_t可以让结构体占用的空间更小,这明显就是做嵌入式开发的人写出来的代码,而liteos与alios-things则明显不如它。


内存初始化:

<code>void lv_mem_init(void)
{
#if LV_MEM_CUSTOM == 0

#if LV_MEM_ADR == 0
/*Allocate a large array to store the dynamically allocated data*/
static LV_MEM_ATTR MEM_UNIT work_mem_int[LV_MEM_SIZE / sizeof(MEM_UNIT)];
work_mem = (uint8_t *)work_mem_int;
#else
work_mem = (uint8_t *)LV_MEM_ADR;
#endif

lv_mem_ent_t * full = (lv_mem_ent_t *)work_mem;
full->header.s.used = 0;
/*The total mem size id reduced by the first header and the close patterns */
full->header.s.d_size = LV_MEM_SIZE - sizeof(lv_mem_header_t);
#endif
}/<code>

内存管理仍然是使用一个大的静态数据,然后管理静态数组。可以看出来它的一个属性d_size指的是数据大小。


内存分配:

<code>void * lv_mem_alloc(size_t size)
{

if(size == 0) {
return &zero_mem;
}

#ifdef LV_MEM_ENV64
/*Round the size up to 8*/
if(size & 0x7) {
size = size & (~0x7);
size += 8;
}
#else
/*Round the size up to 4*/
if(size & 0x3) {
size = size & (~0x3);
size += 4;
}
#endif
void * alloc = NULL;

#if LV_MEM_CUSTOM == 0
/*Use the built-in allocators*/
lv_mem_ent_t * e = NULL;

/* Search for a appropriate entry*/
do {
/* Get the next entry*/
e = ent_get_next(e);

/*If there is next entry then try to allocate there*/
if(e != NULL) {
alloc = ent_alloc(e, size);
}
/* End if there is not next entry OR the alloc. is successful*/
} while(e != NULL && alloc == NULL);

#else
/*Use custom, user defined malloc function*/
#if LV_ENABLE_GC == 1 /*gc must not include header*/
alloc = LV_MEM_CUSTOM_ALLOC(size);
#else /* LV_ENABLE_GC */
/*Allocate a header too to store the size*/
alloc = LV_MEM_CUSTOM_ALLOC(size + sizeof(lv_mem_header_t));
if(alloc != NULL) {
((lv_mem_ent_t *)alloc)->header.s.d_size = size;
((lv_mem_ent_t *)alloc)->header.s.used = 1;

alloc = &((lv_mem_ent_t *)alloc)->first_data;
}
#endif /* LV_ENABLE_GC */
#endif /* LV_MEM_CUSTOM */


#if LV_MEM_ADD_JUNK
if(alloc != NULL) memset(alloc, 0xaa, size);
#endif

if(alloc == NULL) LV_LOG_WARN("Couldn't allocate memory");

return alloc;
}/<code>

内存分配会按照找到最有匹配方法来。也就是说,如果找到一个满足需求的空间,则分配出来。

\u0018\u0005

内存释放:

<code>void lv_mem_free(const void * data)
{
if(data == &zero_mem) return;
if(data == NULL) return;

#if LV_MEM_ADD_JUNK
memset((void *)data, 0xbb, lv_mem_get_size(data));
#endif

#if LV_ENABLE_GC == 0
/*e points to the header*/
lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *)data - sizeof(lv_mem_header_t));
e->header.s.used = 0;
#endif

#if LV_MEM_CUSTOM == 0
#if LV_MEM_AUTO_DEFRAG
/* Make a simple defrag.
* Join the following free entries after this*/
lv_mem_ent_t * e_next;
e_next = ent_get_next(e);
while(e_next != NULL) {
if(e_next->header.s.used == 0) {
e->header.s.d_size += e_next->header.s.d_size + sizeof(e->header);
} else {
break;
}
e_next = ent_get_next(e_next);
}
#endif

#else /*Use custom, user defined free function*/
#if LV_ENABLE_GC == 0
LV_MEM_CUSTOM_FREE(e);
#else
LV_MEM_CUSTOM_FREE((void *)data);
#endif /*LV_ENABLE_GC*/
#endif
}/<code>

内存释放是先将结构体内容先重新复位。接着需要将内存碎片进行整合。它有一个缺陷是,只能往后方整合,而不能向前整合。例如,空闲(A)+占用(B)+空闲(C),如果释放中间的占用(B),那么BC会组合起来,而不会组合ABC,这里就是一个问题点。


内存重新分配:

<code>void * lv_mem_realloc(void * data_p, size_t new_size)
{
/*data_p could be previously freed pointer (in this case it is invalid)*/
if(data_p != NULL) {
lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *)data_p - sizeof(lv_mem_header_t));
if(e->header.s.used == 0) {
data_p = NULL;
}
}

uint32_t old_size = lv_mem_get_size(data_p);
if(old_size == new_size) return data_p; /*Also avoid reallocating the same memory*/

#if LV_MEM_CUSTOM == 0
/* Truncate the memory if the new size is smaller. */
if(new_size < old_size) {
lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *)data_p - sizeof(lv_mem_header_t));
ent_trunc(e, new_size);
return &e->first_data;
}
#endif

void * new_p;
new_p = lv_mem_alloc(new_size);


if(new_p != NULL && data_p != NULL) {
/*Copy the old data to the new. Use the smaller size*/
if(old_size != 0) {
memcpy(new_p, data_p, LV_MATH_MIN(new_size, old_size));
lv_mem_free(data_p);
}
}

if(new_p == NULL) LV_LOG_WARN("Couldn't allocate memory");

return new_p;
}/<code>

这个函数是将内存重新进行分配大小。

内存碎片重新整理:

<code>void lv_mem_defrag(void)
{
#if LV_MEM_CUSTOM == 0
lv_mem_ent_t * e_free;
lv_mem_ent_t * e_next;
e_free = ent_get_next(NULL);

while(1) {
/*Search the next free entry*/
while(e_free != NULL) {
if(e_free->header.s.used != 0) {
e_free = ent_get_next(e_free);
} else {
break;
}
}

if(e_free == NULL) return;

/*Joint the following free entries to the free*/
e_next = ent_get_next(e_free);
while(e_next != NULL) {
if(e_next->header.s.used == 0) {
e_free->header.s.d_size += e_next->header.s.d_size + sizeof(e_next->header);
} else {
break;
}

e_next = ent_get_next(e_next);
}

if(e_next == NULL) return;

/*Continue from the lastly checked entry*/
e_free = e_next;
}
#endif
}/<code>

这个函数是将整个内存重新整理一遍,能合并的就执行合并操作。

内存监测:

<code>void lv_mem_monitor(lv_mem_monitor_t * mon_p)
{
/*Init the data*/
memset(mon_p, 0, sizeof(lv_mem_monitor_t));
#if LV_MEM_CUSTOM == 0
lv_mem_ent_t * e;
e = NULL;

e = ent_get_next(e);

while(e != NULL) {
if(e->header.s.used == 0) {
mon_p->free_cnt++;
mon_p->free_size += e->header.s.d_size;
if(e->header.s.d_size > mon_p->free_biggest_size) {
mon_p->free_biggest_size = e->header.s.d_size;
}
} else {
mon_p->used_cnt++;
}

e = ent_get_next(e);
}
mon_p->total_size = LV_MEM_SIZE;
mon_p->used_pct = 100 - (100U * mon_p->free_size) / mon_p->total_size;
mon_p->frag_pct = (uint32_t)mon_p->free_biggest_size * 100U / mon_p->free_size;
mon_p->frag_pct = 100 - mon_p->frag_pct;
#endif
}
/<code>


这个函数是将整个内存空间从前到后监测一遍,以统计空闲空间是多少,最大的空闲块是多少等等。


获取内存区大小:

<code>uint32_t lv_mem_get_size(const void * data)
{
if(data == NULL) return 0;
if(data == &zero_mem) return 0;

lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *)data - sizeof(lv_mem_header_t));

return e->header.s.d_size;
}/<code>


这个函数是获取指定内存地址处内存大小。

获取下一个内存块:

<code>static lv_mem_ent_t * ent_get_next(lv_mem_ent_t * act_e)
{
lv_mem_ent_t * next_e = NULL;

if(act_e == NULL) { /*NULL means: get the first entry*/
next_e = (lv_mem_ent_t *)work_mem;
} else { /*Get the next entry */
uint8_t * data = &act_e->first_data;
next_e = (lv_mem_ent_t *)&data[act_e->header.s.d_size];

if(&next_e->first_data >= &work_mem[LV_MEM_SIZE]) next_e = NULL;
}


return next_e;
}/<code>


这个函数是根据当前内存块地址获取到下一个内存块地址。

获取内存数据区地址:

<code>static void * ent_alloc(lv_mem_ent_t * e, size_t size)
{
void * alloc = NULL;

//printf("sizeof(lv_mem_header_t)=%d\\n",sizeof(lv_mem_header_t));

/*If the memory is free and big enough then use it */
if(e->header.s.used == 0 && e->header.s.d_size >= size) {
/*Truncate the entry to the desired size */
ent_trunc(e, size),

e->header.s.used = 1;

/*Save the allocated data*/
alloc = &e->first_data;
}

return alloc;
}/<code>


这个函数是用来获取实际返回给用户的内存数据区地址。


内存截取函数:

<code>static void ent_trunc(lv_mem_ent_t * e, size_t size) 

{
#ifdef LV_MEM_ENV64
/*Round the size up to 8*/
if(size & 0x7) {
size = size & (~0x7);
size += 8;
}
#else
/*Round the size up to 4*/
if(size & 0x3) {
size = size & (~0x3);
size += 4;
}
#endif

/*Don't let empty space only for a header without data*/
if(e->header.s.d_size == size + sizeof(lv_mem_header_t)) {
size = e->header.s.d_size;
}

/* Create the new entry after the current if there is space for it */
if(e->header.s.d_size != size) {
uint8_t * e_data = &e->first_data;
lv_mem_ent_t * after_new_e = (lv_mem_ent_t *)&e_data[size];
after_new_e->header.s.used = 0;
after_new_e->header.s.d_size = e->header.s.d_size - size - sizeof(lv_mem_header_t);
}

/* Set the new size for the original entry */
e->header.s.d_size = size;
}/<code>


这个函数是根据参数size来对当前内存大小重新指定与设置一个内存块的相关信息。


这一节就讲完了,可见littlevgl在这块写的确实很简单。


分享到:


相關文章: