[013] [RT thread learning notes] dynamic memory heap management

RT thread version: 4.0.5
MCU model: STM32F103RCT6 (ARM Cortex-M3 core)

introduction

stay Program memory distribution It is explained that the space from the end address of ZI segment to the tail of RAM memory is RTT dynamic memory heap. This paper mainly describes the RT thread dynamic memory heap management algorithm, analyzes the relevant API source code, and gives the example code to verify.

1. Bare metal system dynamic memory

In the startup file startup_stm32f103xe.s:

;1-Configuration stack: Variables(local/overall situation), function call
Stack_Size      EQU     0x00000400  ;1KB

                AREA    STACK, NOINIT, READWRITE, ALIGN=3  ;Allocating stack space does not initialize read / write 8(2^3)byte alignment 
Stack_Mem       SPACE   Stack_Size  
__initial_sp  ;Stack top address

;2-Configure heap for dynamic memory allocation(malloc)                                                
Heap_Size       EQU     0x00000200  ;512Byte

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

2 functional features of memory management

In real-time systems, time requirements are very strict, and memory management is often much more demanding than general operating systems:

  • The time to allocate memory must be fixed. If the time spent looking for a free memory block is uncertain, the response of real-time tasks to external events will also become uncertain for real-time systems.
  • Memory fragmentation problem. With the continuous allocation and release of memory, more and more fragments will be generated in the whole memory area, and there is enough free memory in the system. However, because their addresses are not continuous, they cannot form a continuous and complete memory block, which will make the program unable to apply for large memory. (for general-purpose systems, this inappropriate memory allocation algorithm can be solved by restarting the system (once a month or several months))
  • Select the appropriate memory allocation algorithm according to different embedded system resources.

3 dynamic memory heap management algorithm

  • Small memory management algorithm

Enable macro RT_USING_SMALL_MEM.

For systems with relatively few system resources, it is generally used for systems with less than 2MB memory space (for small memory blocks). When applying, a matching small memory block is divided into a large and continuous memory as required; When released, it is returned to the heap management system. Each memory block contains a data header for management, and the used block and free block are managed through a two-way linked list.

Reference article: Dynamic memory management,Linked list heap manager,Small memory algorithm

  • slab management algorithm

Enable macro RT_USING_HEAP.

When the system resources are abundant, a fast algorithm similar to the multi memory pool management algorithm (for large memory blocks) is provided.

Reference article: slab algorithm

  • memheap management algorithm

Enable macro RT_USING_MEMHEAP_AS_HEAP (after memheap is turned on, the original heap function will be turned off, that is, one of the first two algorithms and the third algorithm).

It is applicable to the case that there are multiple memory heaps in the system. Multiple memories can be "pasted" together to form a large memory heap (for multiple memory heaps).

See: Memory heap management

4 dynamic memory heap related API s

4.1 initialization

  • void rt_system_heap_init(void* begin_addr, void* end_addr)
void rt_system_heap_init(void *begin_addr, void *end_addr)
{
    //1 align the start and end addresses of the memory heap according to the set bytes (some CPUs must access the memory in alignment, and some CPUs can access the memory in non alignment, but the efficiency is low)
    rt_ubase_t begin_align = RT_ALIGN((rt_ubase_t)begin_addr, RT_ALIGN_SIZE);
    rt_ubase_t end_align   = RT_ALIGN_DOWN((rt_ubase_t)end_addr, RT_ALIGN_SIZE);
    
    //2. Since the initialized memory heap is not allocated, there is only one memory block (including a data header) and the data header at the end of the memory heap (used to mark the end of the memory heap)
    //  Therefore, the minimum space of a memory heap needs to accommodate two data headers
    if ((end_align > (2 * SIZEOF_STRUCT_MEM)) &&((end_align - 2 * SIZEOF_STRUCT_MEM) >= begin_align))
    {
        //2.1 calculate the size of the actual data area of the initial heap. (the subsequent data area will be smaller and smaller, because each newly allocated memory block requires a data header space)
        mem_size_aligned = end_align - begin_align - 2 * SIZEOF_STRUCT_MEM;
    }
    else
    {
        //2.2 the memory heap space is too small. Initialization failed
        rt_kprintf("mem init, error begin address 0x%x, and end address 0x%x\n",
                   (rt_ubase_t)begin_addr, (rt_ubase_t)end_addr);
        return;
    }
    
    //3 initialize the header of the first memory block
    heap_ptr = (rt_uint8_t *)begin_align;
    mem        = (struct heap_mem *)heap_ptr;
    mem->magic = HEAP_MAGIC;
    //3.1 this value is the address offset of the next memory block. Since no allocation is made, the whole data area belongs to the first memory block, so it directly points to heap_end
    mem->next  = mem_size_aligned + SIZEOF_STRUCT_MEM;
    mem->prev  = 0;
    mem->used  = 0;
    
    //4 initialize memory heap, end header, heap_end is a global variable, which can be used to judge the overflow of memory heap
    heap_end        = (struct heap_mem *)&heap_ptr[mem->next];
    heap_end->magic = HEAP_MAGIC;
    heap_end->used  = 1;
    //The two-way linked list itself points to 1.4_ end
    heap_end->next  = mem_size_aligned + SIZEOF_STRUCT_MEM;
    heap_end->prev  = mem_size_aligned + SIZEOF_STRUCT_MEM;
    
    //5 initialization semaphore, which is used for thread mutual exclusion when memory application is released
    rt_sem_init(&heap_sem, "heap", 1, RT_IPC_FLAG_PRIO);
    
    //6 lfree is a global chain header, which always points to the first free block
    lfree = (struct heap_mem *)heap_ptr;
}

When using memory heap, the heap must be initialized during system initialization. Automatically initialized by the system, in RT_ hw_ board_ Called in init function:

// Image$$RW_IRAM1$$ZI$$Limit is the symbol exported by the linker, representing the end address of the ZI segment
// Conversely, it is the starting address of the unused RAM area of the execution area, which is used as the starting address of the dynamic memory heap
#define HEAP_BEGIN  ((void *)&Image$$RW_IRAM1$$ZI$$Limit)
#define STM32_SRAM_SIZE 64 										//  The on-chip RAM size is 64k
#define STM32_SRAM_END (0x20000000 + STM32_SRAM_SIZE * 1024) 	//  End address of on-chip RAM
#define HEAP_END    STM32_SRAM_END	

rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END);
  • rt_err_t rt_memheap_init(struct rt_memheap *memheap,

    ​                                            const char *name,

      ​                                          void *start_addr,

    ​   ​                                          rt_uint32_t size)

When using memheap heap memory, call this API. If there are multiple discontinuous memheap, you can call this function multiple times to initialize it and add it to memheap_item linked list.

Memheap: memheap control block

Name: name of memory heap

start_addr: start address of heap memory area

Size: heap memory size

4.2 distribution and release

  • void *rt_malloc(rt_size_t nbytes)

Allocate memory blocks of user specified size from the memory heap

nbytes: the size of the memory block to be allocated, in bytes

Return value: allocation succeeded - available address of memory block; Allocation failed - RT_NULL

void *rt_malloc(rt_size_t size)
{
    rt_size_t ptr, ptr2;
    struct heap_mem *mem, *mem2;

    RT_DEBUG_NOT_IN_INTERRUPT;

    if (size == 0)
        return RT_NULL;

    if (size != RT_ALIGN(size, RT_ALIGN_SIZE))
        RT_DEBUG_LOG(RT_DEBUG_MEM, ("malloc size %d, but align to %d\n",
                                    size, RT_ALIGN(size, RT_ALIGN_SIZE)));
    else
        RT_DEBUG_LOG(RT_DEBUG_MEM, ("malloc size %d\n", size));

    /* memory alignment  */
    size = RT_ALIGN(size, RT_ALIGN_SIZE);
    // mem_size_aligned in rt_system_heap_init() initialized, indicating the maximum space that can be applied for
    if (size > mem_size_aligned) 
    {
        RT_DEBUG_LOG(RT_DEBUG_MEM, ("no memory\n"));

        return RT_NULL;
    }

    /* At least 12 bytes per data block*/
    if (size < MIN_SIZE_ALIGNED)
        size = MIN_SIZE_ALIGNED;

    /* Get semaphores. If another thread is releasing or applying for memory blocks, the thread will hang to avoid operation conflict */
    rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
    // Traverse one by one from the first free block (lfree always points to the first free block, heap_ptr always points to the starting address of the memory heap)
    for (ptr = (rt_uint8_t *)lfree - heap_ptr;
         ptr < mem_size_aligned - size;
         ptr = ((struct heap_mem *)&heap_ptr[ptr])->next)
    {
        mem = (struct heap_mem *)&heap_ptr[ptr];    // Gets the header of the memory block

        // The memory is not used and can meet the user's needs. The requested memory size indicates that the memory of the appropriate size requested by the user has been found
        if ((!mem->used) && (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size)
        {
            /* Mem No, at least perfect matching is possible: * mem - > next - (PTR + size of structure MEM) is the user data size */
            if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >=
                (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED))
            {
                /*(In addition to the above, we test whether another struct heap is included_ mem(SIZEOF_STRUCT_MEM)
                * Min Min minimum_ SIZE_ Size of user's data space
                * Split a large block and create an empty remainder. The remainder must be large enough to contain MIN_SIZE_ALIGNED data:
                * if mem-> next - (ptr +(2 * SIZEOF_STRUCT_MEM)) == size,
                * struct heap_mem Yes, but there is no data between mem2 and mem2 - > next
                * We can omit MIN_SIZE_ALIGNED.   We will create an empty memory block
                * Although the data area cannot be saved, when mem - > next is released, the two areas will be merged, resulting in more available memory*/
                ptr2 = ptr + SIZEOF_STRUCT_MEM + size;

                /* Create a header structure */
                mem2       = (struct heap_mem *)&heap_ptr[ptr2];
                mem2->magic = HEAP_MAGIC;
                mem2->used = 0;
                mem2->next = mem->next;
                mem2->prev = ptr;
#ifdef RT_USING_MEMTRACE
                rt_mem_setname(mem2, "    ");
#endif

                /* And insert it between mem and mem - > next */
                mem->next = ptr2;
                mem->used = 1;

                if (mem2->next != mem_size_aligned + SIZEOF_STRUCT_MEM)
                {
                    ((struct heap_mem *)&heap_ptr[mem2->next])->prev = ptr2;
                }
#ifdef RT_MEM_STATS
                used_mem += (size + SIZEOF_STRUCT_MEM);
                if (max_mem < used_mem)
                    max_mem = used_mem;
#endif
            }
            else
            {
            /*(mem2 The structure is not suitable for the data space requested by the next user. At this time, it will always be used:
            * If not, we have two unused structures in a row, which we have dealt with before
            * The current memory block is the most suitable for the memory applied by the user, which can be allocated directly.
            *
            * Without splitting, mem cannot be moved without mem2 creation
            * Next, go directly after MEM, because mem - > next will always be used at this point!
            */
                mem->used = 1;
#ifdef RT_MEM_STATS
                used_mem += mem->next - ((rt_uint8_t *)mem - heap_ptr);
                if (max_mem < used_mem)
                    max_mem = used_mem;
#endif
            }
            /* Sets the number of changes in the memory block header */
            mem->magic = HEAP_MAGIC;
#ifdef RT_USING_MEMTRACE
            if (rt_thread_self())
                rt_mem_setname(mem, rt_thread_self()->name);
            else
                rt_mem_setname(mem, "NONE");
#endif

            if (mem == lfree)
            {
                /* Find the next free block and update the lfree pointer*/
                while (lfree->used && lfree != heap_end)
                    lfree = (struct heap_mem *)&heap_ptr[lfree->next];

                RT_ASSERT(((lfree == heap_end) || (!lfree->used)));
            }

            rt_sem_release(&heap_sem);
            RT_ASSERT((rt_uint32_t)mem + SIZEOF_STRUCT_MEM + size <= (rt_uint32_t)heap_end);
            RT_ASSERT((rt_uint32_t)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM) % RT_ALIGN_SIZE == 0);
            RT_ASSERT((((rt_uint32_t)mem) & (RT_ALIGN_SIZE - 1)) == 0);

            RT_DEBUG_LOG(RT_DEBUG_MEM,
                         ("allocate memory at 0x%x, size: %d\n",
                          (rt_uint32_t)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM),
                          (rt_uint32_t)(mem->next - ((rt_uint8_t *)mem - heap_ptr))));
			// After the malloc is allocated, its hook function is called.
            RT_OBJECT_HOOK_CALL(rt_malloc_hook,
                                (((void *)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM)), size));

            /* Returns memory data other than the memory block header structure */
            return (rt_uint8_t *)mem + SIZEOF_STRUCT_MEM;
        }
    }

    rt_sem_release(&heap_sem);

    return RT_NULL;
}

Note: the size of each data block is at least MIN_SIZE_ALIGNED=12 (each memory block contains a 12 byte header to store magic, used information and linked list nodes), and must be RT_ALIGN_SIZE=4 byte alignment.

  • void rt_free (void *ptr)

Free memory block

ptr: pointer of memory block to be released

void rt_free(void *rmem)
{
    struct heap_mem *mem;

    RT_DEBUG_NOT_IN_INTERRUPT;

    if (rmem == RT_NULL)
        return;

    RT_OBJECT_HOOK_CALL(rt_free_hook, (rmem));  // Call its hook function before free release.

    /*Check whether the address of rmem belongs to the memory range managed by the system. If rmem
      If the address is smaller than the start address managed by the system or larger than the end address managed by the system, it will be returned illegally*/
    if ((rt_uint8_t *)rmem < (rt_uint8_t *)heap_ptr ||
        (rt_uint8_t *)rmem >= (rt_uint8_t *)heap_end)
    {
        RT_DEBUG_LOG(RT_DEBUG_MEM, ("illegal memory\n"));

        return;
    }

    //1 get the data header of the memory block
    mem = (struct heap_mem *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM);

    if (!mem->used || mem->magic != HEAP_MAGIC)
    {
        rt_kprintf("to free a bad data block:\n");
        rt_kprintf("mem: 0x%08x, used flag: %d, magic code: 0x%04x\n", mem, mem->used, mem->magic);
    }
    
    //2 get semaphores. If other threads are releasing or applying for memory blocks, the thread will hang to avoid operation conflict
    rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
    
    //3 Update header
    mem->used  = 0; // Release the memory and change used to 0, indicating that the memory is not used, but the real data of the memory is not released
    mem->magic = HEAP_MAGIC;
#ifdef RT_USING_MEMTRACE
    rt_mem_setname(mem, "    ");
#endif   
    //4 update the lfree to ensure that the lfree always points to the first free block
    if (mem < lfree)
    {
        lfree = mem;
    }
#ifdef RT_MEM_STATS
    used_mem -= (mem->next - ((rt_uint8_t *)mem - heap_ptr));
#endif  
    //5 check whether adjacent memory blocks are free. If so, try merging
    plug_holes(mem);
    
    rt_sem_release(&heap_sem);
}
  • plug_holes(mem)

When releasing memory or reallocating memory, check whether the two adjacent memory blocks of the released memory block are free (mem - > used = 0). If they are free, they will be merged into a large memory block.

static void plug_holes(struct heap_mem *mem)
{
    struct heap_mem *nmem;
    struct heap_mem *pmem;
    RT_ASSERT((rt_uint8_t *)mem >= heap_ptr);
    RT_ASSERT((rt_uint8_t *)mem < (rt_uint8_t *)heap_end);
    RT_ASSERT(mem->used == 0);
    /* Forward sorting */
    nmem = (struct heap_mem *)&heap_ptr[mem->next];
    if (mem != nmem &&
        nmem->used == 0 &&
        (rt_uint8_t *)nmem != (rt_uint8_t *)heap_end)
    {
        /*If mem - > next is idle and is not a tail node, merge*/
        if (lfree == nmem)
        {
            lfree = mem;
        }
        mem->next = nmem->next;
        ((struct heap_mem *)&heap_ptr[nmem->next])->prev = (rt_uint8_t *)mem - heap_ptr;
    }
    /* Backward finishing */
    pmem = (struct heap_mem *)&heap_ptr[mem->prev];
    if (pmem != mem && pmem->used == 0)
    {
        /* If mem - > prev is idle, merge mem and mem - > prev */
        if (lfree == mem)
        {
            lfree = pmem;
        }
        pmem->next = mem->next;
        ((struct heap_mem *)&heap_ptr[mem->next])->prev = (rt_uint8_t *)pmem - heap_ptr;
    }
}

4.3 redistribution

  • void *rt_realloc(void *rmem, rt_size_t newsize)

Reallocate the size of the memory block based on the allocated memory block (increase or decrease)

rmem: points to the allocated memory block

New size: reallocated memory size

Return value: the same as rt_malloc

void *rt_realloc(void *rmem, rt_size_t newsize)
{
    rt_size_t size;
    rt_size_t ptr, ptr2;
    struct heap_mem *mem, *mem2;
    void *nmem;

    RT_DEBUG_NOT_IN_INTERRUPT;

    /* Memory alignment, 4 bytes */
    newsize = RT_ALIGN(newsize, RT_ALIGN_SIZE);
    //1 exceeds the maximum allocable memory
    if (newsize > mem_size_aligned) 
    {
        RT_DEBUG_LOG(RT_DEBUG_MEM, ("realloc: out of memory\n"));

        return RT_NULL;
    }
    //2. Return RT directly when the size is 0_ NULL
    else if (newsize == 0)  
    {
        rt_free(rmem);
        return RT_NULL;
    }

    //3. If the incoming address is empty, directly allocate a new memory block
    if (rmem == RT_NULL)
        return rt_malloc(newsize);

    rt_sem_take(&heap_sem, RT_WAITING_FOREVER);

    //4 the incoming address exceeds the dynamic memory heap boundary and returns itself
    if ((rt_uint8_t *)rmem < (rt_uint8_t *)heap_ptr ||  
        (rt_uint8_t *)rmem >= (rt_uint8_t *)heap_end)
    {
        /* Illegal memory */
        rt_sem_release(&heap_sem);

        return rmem;
    }

    mem = (struct heap_mem *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM);

    ptr = (rt_uint8_t *)mem - heap_ptr;
    size = mem->next - ptr - SIZEOF_STRUCT_MEM; // Calculate user data size
    //5. The appropriate memory block is the same as the original memory size and returns itself
    if (size == newsize)
    {
        rt_sem_release(&heap_sem);

        return rmem;
    }
    //6 reduce memory
    if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE < size)
    {
        /* split memory block */
#ifdef RT_MEM_STATS
        used_mem -= (size - newsize);
#endif

        ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
        mem2 = (struct heap_mem *)&heap_ptr[ptr2];
        mem2->magic = HEAP_MAGIC;
        mem2->used = 0;
        mem2->next = mem->next;
        mem2->prev = ptr;
#ifdef RT_USING_MEMTRACE
        rt_mem_setname(mem2, "    ");
#endif
        mem->next = ptr2;
        if (mem2->next != mem_size_aligned + SIZEOF_STRUCT_MEM)
        {
            ((struct heap_mem *)&heap_ptr[mem2->next])->prev = ptr2;
        }
        // Check adjacent memory blocks and merge if free memory
        plug_holes(mem2);

        rt_sem_release(&heap_sem);

        return rmem;
    }
    rt_sem_release(&heap_sem);
    //7 expand memory
    nmem = rt_malloc(newsize);  
    if (nmem != RT_NULL) /* check memory */
    {
        rt_memcpy(nmem, rmem, size < newsize ? size : newsize); // copy original memory data
        rt_free(rmem);  // Free original memory
    }

    return nmem;
}

Illegal:

  1. newsize: the maximum memory that RT can allocate is exceeded_ NULL
  2. newsize = 0: return RT_NULL
  3. Empty incoming address: directly allocate a new memory block and return it
  4. Incoming address out of bounds: return its address directly

Legal:

  1. Memory is the same: return its address directly without reallocation
  2. Reduce the memory: the original memory address remains unchanged, and the data is truncated: part of the memory at the end of the memory is removed, and the original content of the remaining part of the memory remains
  3. Expand memory: reallocate memory, copy the original memory data, and then release the original memory, but the new memory is not initialized
  • void *rt_calloc(rt_size_t count, rt_size_t size)

Multiple memory blocks (count size memory blocks) that allocate consecutive memory addresses from the memory heap

count: number of memory blocks

Size: memory block size

Return value: allocation succeeded - pointer to the address of the first memory block; Allocation failed - RT_NULL

rt_calloc is based on rt_malloc implements:

void *rt_calloc(rt_size_t count, rt_size_t size)
{
    void *p;
    RT_DEBUG_NOT_IN_INTERRUPT;	// Cannot be called in an interrupt
    
    p = rt_malloc(count * size);
    
    /* Memory value reset */
    if (p)
        rt_memset(p, 0, count * size);

    return p;
}

4.4 setting memory hook function

  • void rt_malloc_sethook(void (*hook)(void *ptr, rt_size_t size))

The set hook function will call back after the memory allocation is completed. During the callback, the address and size of the allocated memory block will be passed in.

  • void rt_free_sethook(void (*hook)(void *ptr))

The hook function set will call back before the memory release is completed. During the callback, the address of the released memory block will be passed in.

5 dynamic memory considerations

  • Do not allocate or release dynamic memory blocks in the interrupt service routine: because the memory heap manager uses semaphores to protect the critical area in order to meet the safe allocation in the case of multithreading, which may cause the current context to be suspended and wait.
  • Memory reset: every time a new memory block is applied, it is recommended to clear the value of the applied memory. Because the dynamic memory space is shared, a section of memory applied from it may store the data left over from the original application, that is, the initial value is not necessarily 0, which can be used_ Memset reset
  • Memory leak: refers to that when there is something in the heap that has been dynamically allocated in the program that does not need to be used, the program does not release or cannot release (or only release part of the memory, such as rt_free(ptr + 4)), resulting in a waste of system memory, which will slow down the running speed of the program and even cause serious consequences such as system crash (rt_malloc is used together with rt_free).
  • When freeing up memory, do not add memory to rt_free passes an_ The malloc function returns a pointer to the, and do not access dynamic memory after it has been freed.
  • The return address of heap allocated space can only be received by local variables (pointers).

6 example code

#include <rtthread.h>
#define my_printf(fmt, ...)         rt_kprintf("[%u]"fmt"\n", rt_tick_get(), ##__VA_ARGS__)

#define THREAD_PRIORITY      25
#define THREAD_STACK_SIZE    512
#define THREAD_TIMESLICE     5

void malloc_hook(void *ptr, rt_size_t size)
{
    my_printf("malloc_hook: addr:%08x, size:%d byte", ptr, size);
}
void free_hook(void *ptr)
{
    my_printf("free_hook: addr:%08x", ptr);
}
/* Thread entry */
void thread1_entry(void *parameter)
{
    int i;
    char *ptr = RT_NULL; /* Pointer to memory block */

    rt_malloc_sethook(malloc_hook); // malloc hook function (callback after memory allocation)
    rt_free_sethook(free_hook);     // free hook function (callback before memory is fully released)
	my_printf("-----------malloc start------------");
    for (i = 0; ; i++)  // 1 << i 
    {
        /* Allocate memory space of 2^i bytes at a time */
        ptr = rt_malloc(1 << i);        // Insufficient space return RT_NULL

        /* If the assignment is successful */
        if (ptr != RT_NULL)
        {
            my_printf("get memory :%d byte", (1 << i));

            rt_memset(ptr, 0, 1 << i);  // Reset, there may be data left over from the original application stored in the memory
            /* Free memory block */
            rt_free(ptr);
            my_printf("free memory :%d byte", (1 << i));
            ptr = RT_NULL;
            my_printf("-----------------------");
        }
        else
        {
            my_printf("try to get %d byte memory failed!", (1 << i));
            my_printf("-----------malloc end------------");
            
			my_printf("-----------rt_calloc and rt_realloc start------------");
            ptr = rt_calloc(10, 128);   // Multiple memory blocks that allocate contiguous memory addresses
            my_printf("rt_calloc: %08x", ptr);
            
            ptr = rt_realloc(ptr, 128 * 10); // The same as the original memory size: directly return its address without reallocation
            my_printf("rt_realloc-not change: %08x", ptr);

            ptr = rt_realloc(ptr, 128 * 8);  // Shrink: the original memory address remains unchanged and the data is truncated
            my_printf("rt_realloc-reduce: %08x", ptr);

            ptr = rt_realloc(ptr, 128 * 12); // Expand: reallocate memory, copy the original memory data, and then release the original memory
            my_printf("rt_realloc-expand: %08x", ptr);
			
            rt_free(ptr);
			my_printf("-----------rt_calloc and rt_realloc end------------");
			
            return;
        }
    }
}

int dynmem_sample(void)
{
    rt_thread_t tid;

    /* Create thread 1 */
    tid = rt_thread_create("thread1",
                           thread1_entry, RT_NULL,
                           THREAD_STACK_SIZE,
                           THREAD_PRIORITY,
                           THREAD_TIMESLICE);
    if (tid != RT_NULL)
        rt_thread_startup(tid);

    return 0;
}
/* Export to msh command list */
MSH_CMD_EXPORT(dynmem_sample, dynmem sample);

The serial port printing information is as follows:

[233]-----------malloc start------------
[234]malloc_hook: addr:20005d38, size:12 byte
[234]get memory :1 byte
[234]free_hook: addr:20005d38
[235]free memory :1 byte
[235]-----------------------
[235]malloc_hook: addr:20005d38, size:12 byte
[235]get memory :2 byte
[236]free_hook: addr:20005d38
[236]free memory :2 byte
[236]-----------------------
[236]malloc_hook: addr:20005d38, size:12 byte
[237]get memory :4 byte
[237]free_hook: addr:20005d38
[237]free memory :4 byte
[238]-----------------------
[238]malloc_hook: addr:20005d38, size:12 byte
[238]get memory :8 byte
[238]free_hook: addr:20005d38
[239]free memory :8 byte
[239]-----------------------
[239]malloc_hook: addr:20005d38, size:16 byte
[240]get memory :16 byte
[240]free_hook: addr:20005d38
[240]free memory :16 byte
[240]-----------------------
[241]malloc_hook: addr:20005d38, size:32 byte
[241]get memory :32 byte
[241]free_hook: addr:20005d38
[241]free memory :32 byte
[242]-----------------------
[242]malloc_hook: addr:20005d38, size:64 byte
[242]get memory :64 byte
[243]free_hook: addr:20005d38
[243]free memory :64 byte
[243]-----------------------
[243]malloc_hook: addr:20005d38, size:128 byte
[244]get memory :128 byte
[244]free_hook: addr:20005d38
[244]free memory :128 byte
[245]-----------------------
[245]malloc_hook: addr:20005d38, size:256 byte
[245]get memory :256 byte
[245]free_hook: addr:20005d38
[246]free memory :256 byte
[246]-----------------------
[246]malloc_hook: addr:20005d38, size:512 byte
[247]get memory :512 byte
[247]free_hook: addr:20005d38
[247]free memory :512 byte
[247]-----------------------
[248]malloc_hook: addr:20005d38, size:1024 byte
[248]get memory :1024 byte
[248]free_hook: addr:20005d38
[249]free memory :1024 byte
[249]-----------------------
[249]malloc_hook: addr:20007654, size:2048 byte
[250]get memory :2048 byte
[250]free_hook: addr:20007654
[250]free memory :2048 byte
[250]-----------------------
[251]malloc_hook: addr:20007654, size:4096 byte
[251]get memory :4096 byte
[251]free_hook: addr:20007654
[252]free memory :4096 byte
[252]-----------------------
[252]malloc_hook: addr:20007654, size:8192 byte
[252]get memory :8192 byte
[253]free_hook: addr:20007654
[253]free memory :8192 byte
[253]-----------------------
[253]malloc_hook: addr:20007654, size:16384 byte
[254]get memory :16384 byte
[254]free_hook: addr:20007654
[254]free memory :16384 byte
[255]-----------------------
[255]malloc_hook: addr:20007654, size:32768 byte
[255]get memory :32768 byte
[256]free_hook: addr:20007654
[256]free memory :32768 byte
[256]-----------------------
[256]try to get 65536 byte memory failed!
[257]-----------malloc end------------
[257]-----------rt_calloc and rt_realloc start------------
[258]malloc_hook: addr:20005d38, size:1280 byte
[258]rt_calloc: 20005d38
[258]rt_realloc-not change: 20005d38
[259]rt_realloc-reduce: 20005d38
[259]malloc_hook: addr:20007654, size:1536 byte
[259]free_hook: addr:20005d38
[260]rt_realloc-expand: 20007654
[260]free_hook: addr:20007654
[260]-----------rt_calloc and rt_realloc end------------
[261]free_hook: addr:20005b2c
[261]free_hook: addr:20005a8c
  • rt_malloc test: when trying to apply for 65536 byte s, i.e. 64KB memory, the allocation failed because the total size of ram is only 64K and the available RAM is less than 64K.
  • rt_calloc and rt_realloc test verifies the contents of the code comments.

END

Keywords: memory management rt-thread RTOS

Added by andreiga on Fri, 11 Feb 2022 08:31:16 +0200