文章目錄
- 0. 概要
- 1. mmap基礎
- 2. 為什么使用內存池?
- 3. 使用示例
- 4. 設計考慮
- 5. 實現步驟
0. 概要
c語言基于mmap實現的內存池模塊
倉庫地址: https://gitee.com/liudegui/mem_allocator_c
1. mmap基礎
mmap
系統調用在進程的虛擬地址空間和某個文件對象或匿名存儲之間建立映射關系。當使用mmap
進行內存映射時,操作系統會創建一個映射,使得對這段內存的訪問就像對文件的讀寫一樣。
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
addr
:建議的映射起始地址(通常設為NULL,由系統決定)。length
:映射區域的長度。prot
:期望的內存保護標志(如PROT_READ, PROT_WRITE等)。flags
:影響映射區域的各種特性(如MAP_SHARED, MAP_PRIVATE等)。fd
:被映射對象的文件描述符,或者使用-1
來表示匿名映射。offset
:文件映射的起始位置。
2. 為什么使用內存池?
- 提高效率:內存池可以減少向操作系統申請和釋放內存的次數,從而提高內存分配的效率。
- 減少碎片:內存池可以有效地減少內存碎片,提高內存利用率。
- 便于管理:內存池可以將內存的申請、釋放等操作集中管理,方便程序員進行內存調試和維護。
3. 使用示例
下面是一個基于C語言的內存池實現示例:
- 頭文件mem_allocator_c.h
/***Manager usage of vm mapped to /dev/mem 0x1000000~0x1dfffff*When create a MemPool, it's pool_ should be alloc use this class.*It will first alloc firstly try to alloc from memory map to /dev/mem.*If there is not enough memory in /dev/mem 0x1000000~0x1dfffff, it*will try to call calloc() func of stdlib to alloc memory.*/#ifndef UTILS_MEM_ALLOCATOR_C_H_
#define UTILS_MEM_ALLOCATOR_C_H_#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>
#include <stddef.h>#define MEM_ALLOC_DEFAULT 1
#define MEM_ALLOC_MAP 1
#define MEM_FD_INIT -1//=====================================================================
// MemMapMemAllocator
// Use MemMap to map /dev/mem 0x1000000-0x1dffefff into virtual memory.
// Use a linked table to store ranges in this mapping space. A range
// may be free or in-use.
//=====================================================================typedef struct MemAllocator MemAllocator;
typedef struct MemRange MemRange;
typedef struct MMapParams MMapParams;
typedef struct MemAllocDelegate MemAllocDelegate;#define MMAP_BASE_ADDR 0x10000000
#define MMAP_HIGH_ADDR 0x1DFFEFFFstruct MemRange {int8_t* offset;int8_t* end;uint32_t size;bool is_dirty;MemRange* next;MemRange* pre;
};struct MMapParams {MemRange* range_list;int32_t mem_fd;int8_t* map_base;uint32_t page_size;uint32_t base_addr;uint32_t high_addr;
};struct MemAllocator {int8_t* (*calloc)(MemAllocator* allocator, uint32_t nmemb, uint32_t size);void (*free)(MemAllocator* allocator, int8_t* buffer);char allocator_name[32];MMapParams* mmap_params;MemAllocator* next;
};struct MemAllocDelegate{MemAllocator* allocator;
};void init_mem_delegate(MemAllocDelegate* delegate, uint32_t base_addr, uint32_t high_addr);
int8_t* mem_delegate_calloc(MemAllocDelegate* delegate, uint32_t nmemb, uint32_t size);
void mem_delegate_free(MemAllocDelegate* delegate, int8_t* buffer);
void destroy_mem_delegate(MemAllocDelegate* delegate);#endif // UTILS_MEM_ALLOCATOR_C_H_
- 實現代碼見: https://gitee.com/liudegui/mem_allocator_c
#include "mem_allocator_c.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>int main() {printf("hello world");MemAllocDelegate delegate;init_mem_delegate(&delegate, 0, 0);int8_t* p_addr = mem_delegate_calloc(&delegate, 10, 10);const char* const str = "hello world";memcpy(p_addr, str, strlen(str) + 1);printf("%s\n", p_addr);mem_delegate_free(&delegate, p_addr);destroy_mem_delegate(&delegate);return 0;
}
在這個示例中,我們首先定義了一個MemAllocDelegate
結構體,用于存儲內存池的信息。然后,我們通過init_mem_delegate
函數初始化內存池,指定其大小和對齊要求。接下來,我們使用mem_delegate_calloc
函數從內存池中分配一塊內存,并將字符串"hello world"
復制到這塊內存中。最后,我們使用mem_delegate_free
函數釋放這塊內存,并通過destroy_mem_delegate
函數銷毀內存池。
4. 設計考慮
在設計基于mmap
的內存池時,需要考慮以下關鍵因素:
- 內存映射大小 :確定內存映射的大小是至關重要的。太小可能導致頻繁地創建新的映射,而太大則可能造成內存浪費。
- 內存對齊 :確保內存分配滿足特定硬件或軟件的對齊要求。
- 內存保護 :設置合理的內存保護標志以避免越界訪問。
- 內存清理 :在程序結束時,確保所有映射都被正確地釋放。
- 錯誤處理 :正確處理
mmap
調用可能出現的錯誤,例如內存不足。 - 性能監控 :跟蹤內存分配和釋放的性能,以優化內存池的行為。
5. 實現步驟
- 初始化 :
- 定義內存池結構體,包含必要的字段如
mmap_params
、MMapMemAllocator
等。 - 初始化內存映射參數,包括頁大小、映射大小等。
- 調用
mmap
創建初始內存映射區域。
- 定義內存池結構體,包含必要的字段如
- 內存分配 :
- 實現內存分配邏輯,根據請求的大小找到第一個合適的空閑區域。
- 更新內存池的數據結構以反映分配情況。
- 返回分配的內存區域的指針。
- 內存釋放 :
- 根據提供的指針找到對應的內存區域。
- 更新內存池的數據結構以標記該區域為可用。
- 如果有必要,合并相鄰的空閑區域以減少碎片。
- 銷毀 :
- 遍歷內存池的所有映射區域,調用
munmap
釋放每個映射。 - 清理內存池的數據結構。
- 遍歷內存池的所有映射區域,調用