usr 发表于 2020-10-26 14:41:39

【C】简单的malloc和free的实现

这里简单实现了cstdlib的动态内存管理函数malloc、calloc、realloc和free
大致原理是先申请一个数组作为内存空间,然后使用链表连接每个申请的内存块。如果要释放内存,就从链表中移除相应的内存块。
在没有动态内存管理的环境中,以下代码能派上用场替代malloc函数等。
使用前需要先调用mpkInitMemory函数。
这是头文件:

#ifndef _MEMPKG_H_
#define _MEMPKG_H_

#include <stddef.h> /* Using type size_t. */

typedef unsigned char UCHART, * PUCHAR;

typedef struct st_BLOCK_HEADER
{
        struct st_BLOCK_HEADER * pnext;
        PUCHAR pblock;
        size_t size;
} BLOCK_HEADER, * P_BLOCK_HEADER;

#define MEM_SIZ (8192) /* Alter this macro to control the size of a heap. */

void   mpkInitMemory(void);
void * memcpy       (void *       dst, void *       src, size_t size);
void * memset       (void *       s,   int          c,   size_t n);
void * malloc       (size_t       size);
void   free         (void *       ptr);
void * realloc      (void *       ptr, size_t       size);
void * calloc       (size_t       n,   size_t       size);
int    memcmp       (const void * s1,const void * s2,size_t n);

#define memmove(dst, src, size) memcpy(dst, src, size)

#endif

源文件:

#include "mempkg.h"

UCHART volatile array; /* Create a linear address space. */

const PUCHAR volatile pmem = array;

volatile P_BLOCK_HEADER phead = NULL; /* This is the header pointer of block chain. */

/* Invoke this function before you use any function of this package.
*/
void mpkInitMemory(void)
{
        phead = (P_BLOCK_HEADER)pmem;
        phead->pblock = pmem + sizeof(BLOCK_HEADER);
        phead->pnext = (P_BLOCK_HEADER)(pmem + MEM_SIZ - sizeof(BLOCK_HEADER));
        phead->size = 0;
        phead->pnext->pblock = pmem + MEM_SIZ;
        phead->pnext->pnext = NULL;
        phead->pnext->size = 0;
}

void * memcpy(void * dst, void * src, size_t size)
{
        char * sc1;
        const char * sc2;
        sc1 = dst;
        sc2 = src;
        if (sc2 < sc1 && sc1 < sc2 + size)
                for (sc1 += size, sc2 += size; 0 < size; --size)
                        *--sc1 = *--sc2;
        else
                for (; 0 < size; --size)
                        *sc1++ = *sc2++;
        return dst;
}

void * memset(void * s, int c, size_t n)
{
        const UCHART uc = c;
        unsigned char * su;
        for (su = s; 0 < n; ++su, --n)
                *su = uc;
        return s;
}

void * malloc(size_t size)
{
        if (size > 0)
        {
                P_BLOCK_HEADER pnode, pnew;
                for (pnode = phead; NULL != pnode; pnode = pnode->pnext)
                {
                        if (pnode->pnext != NULL)
                        {        /* Test available space for the new block. */
                                if (pnode->pnext->pblock - sizeof(BLOCK_HEADER) - pnode->pblock - pnode->size >=
                                        size + sizeof(BLOCK_HEADER))
                                {
                                        pnew = (P_BLOCK_HEADER)(pnode->pblock + pnode->size);
                                        /* Initialize the new block header and assign new block. */
                                        pnew->pblock = pnode->pblock + pnode->size + sizeof(BLOCK_HEADER);
                                        pnew->size = size;
                                        pnew->pnext = pnode->pnext;
                                        pnode->pnext = pnew;
                                        return pnew->pblock;
                                }
                        }
                        else
                                break;
                }
        }
        return NULL;
}

void free(void * ptr)
{        /* Preserve the tail and header block structure. */
        if (pmem == ptr || pmem + MEM_SIZ == ptr)
                return;
        if (NULL != ptr)
        {
                P_BLOCK_HEADER pnode;
                for (pnode = phead; NULL != pnode; pnode = pnode->pnext)
                {
                        if (NULL != pnode->pnext && ptr == pnode->pnext->pblock)
                        {
                                pnode->pnext = pnode->pnext->pnext;
                                return;
                        }
                }
        }
        return;
}

void * realloc(void * ptr, size_t size)
{        /* Preserve the tail and header block structure. */
        if (pmem == ptr || pmem + MEM_SIZ == ptr)
                return NULL;
        if (NULL == ptr && size > 0)
                return malloc(size);
        else if (size > 0)
        {
                P_BLOCK_HEADER pnode;
                for (pnode = phead; NULL != pnode; pnode = pnode->pnext)
                {
                        if (NULL != pnode->pnext && ptr == pnode->pnext->pblock)
                        {
                                if (size == pnode->pnext->size)
                                        return ptr;
                                else if (pnode->pnext->pnext->pblock - sizeof(BLOCK_HEADER) - pnode->pblock - pnode->size >=
                                        size + sizeof(BLOCK_HEADER))
                                {
                                        pnode->pnext->size = size;
                                        return ptr;
                                }
                                else
                                {
                                        void * newptr = malloc(size);
                                        if (NULL != newptr)
                                        {
                                                memcpy(newptr, ptr, pnode->pnext->size);
                                                free(ptr);
                                        }
                                        return newptr;
                                }
                        }
                }
        }
        return NULL;
}

void * calloc(size_t n, size_t size)
{
        void * rtn = malloc(size * n);
        memset(rtn, 0, size * n);
        return rtn;
}

int memcmp(const void * s1, const void * s2, size_t n)
{
        PUCHAR su1, su2;
        for (su1 = s1, su2 = s2; 0 < n; ++su1, ++su2, --n)
                if (*su1 != *su2)
                        return *su1 < *su2 ? -1 : 1;
        return 0;
}

0xAA55 发表于 2020-10-27 00:27:37

一种裸机上管理内存的感觉,让我想到了在DOS年代,捅穿A20地址线,然后写16 MB内存的一些DOS游戏。

但,像STM32这样的单片机上,然后像STM32CubeIDE这样的开发环境提供的malloc其实是静态分配地址,就像一个宏,它给你局部起了一个static char mem;然后这块内存妥妥地进入了链接器脚本的.bss段。对应的,单片机上的free是没有任何行为的。

另外,现在的桌面机或者服务器环境,我为了实现高频的内存分配释放的效果,写过内存池,然后感觉一个比较大的难点是如何实现多线程环境下,能尽可能不锁住线程,实现高效的内存分配释放。

usr 发表于 2020-10-27 14:01:26

这段代码还有个缺点就是容易形成内存碎片,本来想实现一个伙伴系统的无奈功夫不到家没有实现成功。
在多线程环境下这段代码必须加锁,不然内存会被写乱。
另外,楼上可以参考一下glibc的malloc来获得灵感。
页: [1]
查看完整版本: 【C】简单的malloc和free的实现