内存分配与回收
内存堆
内存堆是内存管理的一种方式,用户可以把一块固定的物理内存交给内存堆管理。在 SylixOS 中内核堆与系统堆都是内存堆的一种。在设备驱动需要反复的申请、释放内存时,为了提高内存的利用率,用户可以申请一块物理内存作为设备驱动专有的内存堆,并可以从这块内存堆里申请。如果用户在编写设备驱动时要管理一块设备 RAM 也可使用内存堆管理。内存堆的主要函数位于“libsylixos/kernel/core/_HeapLib.c”。
创建内存堆
函数 _HeapCreate 的功能是创建内存堆,用户需要提供的参数是需要管理的物理内存首地址和这一块需要申请的内存堆的大小。其详细描述如下:
#include <SylixOS.h>
PLW_CLASS_HEAP _HeapCreate (PVOID pvStartAddress,
size_t stByteSize);
函数 _HeapCreate 原型分析:
- 此函数成功时返回一个内存堆的 PLW_CLASS_HEAP 结构体指针,失败时返回 LW_NULL 。
- 参数 pvStartAddress 是需要管理的物理内存首地址。
- 参数 stByteSize 是申请内存堆的大小。
LW_CLASS_HEAP 是内存堆的结构体,其详细描述如下:
#include <SylixOS.h>
typedef struct {
LW_LIST_MONO HEAP_monoResrcList;
PLW_LIST_RING HEAP_pringFreeSegment;
#if (LW_CFG_SEMB_EN > 0) && (LW_CFG_MAX_EVENTS > 0)
LW_OBJECT_HANDLE HEAP_ulLock;
#endif
PVOID HEAP_pvStartAddress;
size_t HEAP_stTotalByteSize;
ULONG HEAP_ulSegmentCounter;
size_t HEAP_stUsedByteSize;
size_t HEAP_stFreeByteSize;
size_t HEAP_stMaxUsedByteSize;
UINT16 HEAP_usIndex;
CHAR HEAP_cHeapName[LW_CFG_OBJECT_NAME_SIZE];
LW_SPINLOCK_DEFINE (HEAP_slLock);
} LW_CLASS_HEAP;
typedef LW_CLASS_HEAP *PLW_CLASS_HEAP;
- HEAP_monoResrcList:空闲资源表。
- HEAP_pringFreeSegment:空闲段表。
- HEAP_ulLock:信号锁,用于保护内存堆在同一个时段不被多个用户使用。
- HEAP_pvStartAddress:内存堆起始地址。
- HEAP_stTotalByteSize:堆内存总大小。
- HEAP_ulSegmentCounte:分段数量。
- HEAP_stUsedByteSize:使用的字节数量。
- HEAP_stFreeByteSize:空闲的字节数量。
- HEAP_stMaxUsedByteSize:最大使用的字节数量。
- HEAP_usIndex:堆缓冲索引。
- HEAP_cHeapName:堆名。
- HEAP_slLock:自旋锁。
说明:
用户在管理内存堆结构体中的相关成员时,请使用系统提供的管理接口,以便驱动可以向后兼容。
成员 HEAP_monoResrcList 是当前空闲的内存堆链表,用户可遍历链表获取空闲的内存堆的结构体指针;使用完成后,用户可以把内存堆归还给成员 HEAP_monoResrcList,不需要释放内存堆。成员 HEAP_monoResrcList 链表可以看作一个容器链表,使用时可申请容器,不需要时可归还容器。此方式可以减少内存碎片,提高内存利用率。
成员 HEAP_pringFreeSegment 是当前内存堆中空闲内存空间的链表,用户从内存堆中申请字节池时,会从成员 HEAP_pringFreeSegment 链表中获取空闲的内存空间;用户释放字节池时,会把释放的内存空间加入到成员 HEAP_pringFreeSegment 链表。
删除内存堆
函数 _HeapDelete 的功能是删除内存堆,用户需要提供的参数是已创建的内存堆结构体的指针和是否需要检查使用情况。其详细描述如下:
#include <SylixOS.h>
PLW_CLASS_HEAP _HeapDelete (PLW_CLASS_HEAP pheap, BOOL bIsCheckUsed);
函数 _HeapDelete 原型分析:
- 函数成功返回 LW_NULL ,失败返回堆结构体的指针。
- 参数 pheap 是内存堆结构体的指针。
- 参数 bIsCheckUsed 是否检查使用情况。
如果一个内存堆还在被使用时理论上不应该被删除,当用户无法确定该堆是否正在被使用时,需要给参数 bIsCheckUsed 传递 LW_TRUE ,函数 _HeapDelete 会检查内存堆的使用情况:如果参数 pheap 所代表的内存堆没有被使用,则执行删除流程,执行成功时返回 LW_NULL ;执行失败则返回传入的内存堆的结构体指针。
申请字节池
用户成功申请内存堆之后,如果需要从内存堆中申请字节池,可以根据不同的需求执行不同的申请函数,SylixOS 提供三种不同的函数接口,用于满足用户的不同需求。
函数 _HeapAllocate 的功能是从指定的内存堆中申请字节池,用户需要提供的参数是已创建的内存堆结构体的指针、需要申请的字节池的大小和申请字节池的用途。其详细描述如下:
#include <SylixOS.h>
PVOID _HeapAllocate (PLW_CLASS_HEAP pheap,
size_t stByteSize,
CPCHAR pcPurpose);
函数 _HeapAllocate 原型分析:
- 函数成功时返回分配的内存首地址,失败时返回 LW_NULL 。
- 参数 pheap 是内存堆结构体的指针,指定在哪块内存堆中申请字节池。
- 参数 stByteSize 是申请的字节数。
- 参数 pcPurpose 是分配内存的用途。
说明:
分配内存的用途是指描述一下申请的内存的作用,一般写 LW_NULL。
函数 _HeapZallocate 的功能是从指定的内存堆中申请字节池,并且把申请的字节池清零。用户需要提供的参数是已创建的内存堆结构体的指针、需要申请的字节池的大小和申请字节池的用途。其详细描述如下:
#include <SylixOS.h>
PVOID _HeapZallocate (PLW_CLASS_HEAP pheap,
size_t stByteSize,
CPCHAR pcPurpose);
函数 _HeapZallocate 原型分析:
- 函数成功时返回分配的内存首地址,失败时返回 LW_NULL 。
- 参数 pheap 是内存堆结构体的指针,指定在哪块内存堆中申请字节池。
- 参数 stByteSize 是申请的字节数。
- 参数 pcPurpose 是分配内存的用途。
函数 _HeapAllocateAlign 的功能是申请字节对齐的字节池,用户需要提供的参数是已创建的内存堆结构体的指针、需要申请的字节池的大小、内存对齐关系和申请字节池的用途。其详细描述如下:
#include <SylixOS.h>
PVOID _HeapAllocateAlign (PLW_CLASS_HEAP pheap,
size_t stByteSize,
size_t stAlign,
CPCHAR pcPurpose);
函数 _HeapAllocateAlign 原型分析:
- 函数成功时返回分配的内存首地址,失败时返回 LW_NULL 。
- 参数 pheap 是内存堆结构体的指针,指定在哪块内存堆中申请字节池。
- 参数 stByteSize 是申请的字节数。
- 参数 stAlign 是内存对齐关系。
- 参数 pcPurpose 是分配内存的用途。
说明:
内存对齐关系是指用户想要申请的内存是按照多少字节对齐。
用户在开发设备驱动时,可能会遇到特殊的需求,比如设备需要在指定内存区域申请字节对齐的内存空间,这时用户就需要通过调用函数 _HeapAllocateAlign 实现这一功能。当设备要求在指定内存区域申请内存空间,但没有要求字节对齐的时候,用户可以调用函数 _HeapAllocate 或函数 _HeapZallocate 实现。
释放字节池
函数 _HeapFree 的功能是释放字节池,用户需要提供的参数是已创建的内存堆结构体的指针、被释放的字节池的物理地址、是否要检查字节池的安全性和是哪个函数释放的字节池。其详细描述:
#include <SylixOS.h>
PVOID _HeapFree (PLW_CLASS_HEAP pheap,
PVOID pvStartAddress,
BOOL bIsNeedVerify,
CPCHAR pcPurpose);
函数 _HeapFree 原型分析:
- 函数成功时返回 LW_NULL ,失败时返回 pvStartAddress 。
- 参数 pheap 是内存堆的结构体的指针,指定从哪块内存堆中释放字节池。
- 参数 pvStartAddress 是被释放的字节池的物理地址。
- 参数 bIsNeedVerify 是是否要检查安全性。
- 参数 pcPurpose 是哪个函数释放的字节池。
说明:
- 安全性检测是指在释放字节池前需要检查这个字节池是否在指定内存堆中或已被占用。
- 用户使用完申请的字节池后,必须要释放字节池,以避免内存堆的空间被用完。
动态内存申请函数
SylixOS 有自己的内存申请函数,如 sys_malloc、sys_zalloc 等,每个申请函数都有着特定的功能,以下会逐一介绍各个函数的详细信息。
函数 sys_malloc 的功能是申请物理内存,用户需要提供的参数是需要申请的内存的大小。其详细描述如下:
#include <SylixOS.h>
PVOID sys_malloc (size_t szie);
函数 sys_malloc 原型分析:
- 函数成功时返回分配的内存首地址,失败时返回 LW_NULL 。
- 参数 size 是需要申请的内存大小。
函数 sys_zalloc 的功能是申请物理内存,并且初始化所有内存为 0,用户需要提供的参数是需要申请的内存的大小。其详细描述如下:
#include <SylixOS.h>
PVOID sys_zalloc (size_t size);
函数 sys_zalloc 原型分析:
- 函数成功时返回分配的内存首地址,失败时返回 LW_NULL 。
- 参数 size 是需要申请的内存大小。
函数 sys_malloc_align 的功能是申请字节对齐的物理内存,用户需要提供的参数是需要申请的内存的大小和内存的对齐关系。其详细描述如下:
#include <SylixOS.h>
PVOID sys_malloc_align (size_t size, size_t align);
函数 sys_malloc_align 原型分析:
- 函数成功时返回分配的内存首地址,失败时返回 LW_NULL 。
- 参数 size 是需要申请的内存大小。
- 参数 align 是内存对齐关系。
函数 sys_realloc 的功能是改变原申请的物理内存的大小,用户需要提供的参数是原申请的内存的首地址和需要申请的新的内存大小。其详细描述如下:
#include <SylixOS.h>
PVOID sys_realloc (PVOID p, size_t new_size);
函数 sys_realloc 原型分析:
- 函数成功时返回分配的内存首地址,失败时返回 LW_NULL 。
- 参数 p 是原申请的内存的首地址。
- 参数 new_size 是需要申请的新的内存大小。
函数 sys_realloc 可改变原申请的内存的大小,当原内存无法扩展时,系统会释放原内存,并在内存堆中根据首次适应算法申请合适大小的内存空间。用此函数时应当说明意,如果原内存中保存有重要的数据,在使用此函数之前需要把重要的数据保存到其他存储区域,以免数据丢失。
SylixOS 同时兼容 Linux 驱动申请内存的函数,Linux 驱动申请内存函数如下:
#include <SylixOS.h>
PVOID vmalloc (size_t size);
PVOID kmalloc (size_t size, int flags);
PVOID kzalloc (size_t size, int flags);
函数 vmalloc 原型分析:
- 函数成功时返回分配的内存首地址,失败时返回 LW_NULL 。
- 参数 size 是需要申请的内存大小。
函数 kmalloc 原型分析:
- 函数成功时返回分配的内存首地址,失败时返回 LW_NULL 。
- 参数 size 是需要申请的内存大小。
- 参数 flags 是为内存申请的类型,主要内存申请类型如下表所示。
函数 kzalloc 原型分析:
- 函数成功时返回分配的内存首地址,失败时返回 LW_NULL 。
- 参数 size 是需要申请的内存大小。
- 参数 flags 是申请内存的类型,主要内存申请类型如下表所示。
申请内存的类型 | 含义 |
---|---|
GFP_ATOMIC | 用来从中断处理和进程上下文之外的其他代码中分配内存,从不睡眠。 |
GFP_KERNEL | 内核内存的正常分配,可能睡眠。 |
GFP_USER | 用来为用户空间页来分配内存,可能睡眠。 |
GFP_HIGHUSER | 如同 GFP_USER, 但是从高端内存分配。 |
GFP_NOFS | 这个标志功能如同 GFP_KERNEL,但 GFP_NOFS 分配不允许进行任何文件系统调用。 |
GFP_NOIO | 这个标志功能如同 GFP_KERNEL,但 GFP_NOIO 不允许任何 I/O 初始化。 |
在 SylixOS 中函数 vmalloc 和函数 kmalloc 的原型都是 sys_malloc,所以与 Linux 系统不同,函数 vmalloc 和函数 kmalloc 使用起来并没有什么差别。函数 kzalloc 的函数原型为 sys_zalloc,具体功能小节已描述过,这里不再重复。
动态内存释放函数
函数 sys_free 的功能是释放已申请的内存,用户需要提供的参数是已申请内存的首地址。其详细描述如下:
#include <SylixOS.h>
PVOID sys_free (PVOID pvMemory);
函数 sys_free 原型分析:
- 函数正确时返回 LW_NULL ,失败时返回 pvMemory 。
- 参数 pvMemory 是需要释放的内存地址。
SylixOS 兼容 Linux 的内存释放函数,Linux 的内存释放函数如下:
#include <SylixOS.h>
PVOID kfree (PVOID pvMemory);
PVOID vfree (PVOID pvMemory);
函数 kfree 原型分析:
- 函数正确返回 LW_NULL ,失败时返回 pvMemory 。
- 参数 pvMemory 是需要释放的物理内存首地址。
函数 vfree 原型分析:
- 函数正确返回 LW_NULL ,失败时返回 pvMemory 。
- 参数 pvMemory 是需要释放的物理内存首地址。
在 SylixOS 中函数 kfree 与函数 vfree 的原型为函数 sys_free,所以与函数 sys_free 的使用方式相同,这里不再重复描述。