C语言|一种基于linux mmap特性的应用层虚拟内存工具的编写

为了方便一些对时间不敏感的大数据块的存放,尤其是在JNI环境中避免系统OOM,我打算给予MMAP做一套虚拟内存工具:
首先来想一下申请连续块时的各种情况,方便编写仿malloc函数,各种增删情况如图:
C语言|一种基于linux mmap特性的应用层虚拟内存工具的编写
文章图片

每个块开头用一个long来保存块总大小,寻找空间时见到这个标记即可立即跳过相应区域,如果有多个空间块则会因为连续踩到块大小标记产生连续跳跃,而当没有踩到该标记时——即内容为0时,逐地址遍历是否满足空间要求(一个long + 用户要求的大小)的地址内容都为0,是的话则使用该空间,起始地址用long记录占用空间大小,然后偏移一个long大小给用户作为数据保存区。这种逻辑在内存地址中间的空间块被移除时依然有效,如果间隙位置足够则使用间隙位置,不够的话自然会产生跳跃重新在跳跃后寻找足够的空间的地址。
数据结构设计:
块:[块大小标记 long][用户指定大小内存块]
操作时:
malloc:用户传入需要的大小,例如8个字节,函数内将大小需求加大一个sizeof(long)增大一个long的占用空间在开头,用于标记占用量,返回时不返回块开头,而是块开头+sizeof(long)偏移到“用户制定大小内存块”的开头给用户自由发挥。
free:用户传入“用户指定大小内存块”的首地址,然后地址减去sizeof(long)得到真正的数据结构开头,使用“块大小标记”了解整块结构的实际占用空间,然后把整块空间从头到尾清0
(但实际上这样有个漏洞,用户超容量操作时容量引发虚拟内存泄露,这个我暂时认为是无法解决的,用物理内存时也一样……只能使用时规范些)
具体实现代码如下,此为代码草稿,不是正式版,用于思想展示:

/**@author 陈杰柱 for unit/linux,用于映射外存文件 * 作为内存指针,并模拟malloc和free的操作,来模拟内存条, * 作为虚拟内存使用,可以同时使用多个虚拟内存条**/#include "stdio.h" #include "stdlib.h" #include #include #include #include #include #include /* 虚拟内存条 @param fileMapAddress 内存条起始地址 @param size 内存条大小 */ typedef struct memoryUnit { void *fileMapAddress; unsigned long fileHandle; size_t size; } MemoryUnit; MemoryUnit *createVirtualMemory(char *filePath, size_t size, int createFile); void *fileMapMalloc(MemoryUnit *mem, size_t size); int fileMapObjectSize(void *p); int fileMapFree(void *p); int destroyVirtualMemory(MemoryUnit *m); /* 创造虚拟内存条 @param filePath 虚拟内存条在外存的地址 @param size 设定内存条大小 @return 返回内存条对象 */ MemoryUnit *createVirtualMemory(char *filePath, size_t size, int createFile) { if(createFile > 0) { //创建制定大小的虚拟内存文件 if (creat(filePath, 0755) < 0) { printf("create file %s failure!\n", filePath); return NULL; } else { printf("create file %s successed!\n", filePath); } } unsigned long f = open(filePath, O_RDWR); if (lseek(f, size - 1, SEEK_SET) < 0) { return NULL; } else { char data[1] = {0}; write(f, &data, 1); lseek(f, 0, SEEK_SET); printf("open file %s successed!\n", filePath); //创建内存映射 //map the file start position as a FAKE memory address. MemoryUnit *unit = (MemoryUnit *)malloc(sizeof(MemoryUnit)); unit->fileHandle = f; unit->fileMapAddress = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, f, 0); unit->size = size; if (unit->fileMapAddress == MAP_FAILED) { free(unit); return NULL; } return unit; } }/* 从虚拟内存条中获取指定大小的连续空间 @param mem 虚拟内存条 @param size 需要分配多大空间 @return 返回连续空间地址 */ void* fileMapMalloc(MemoryUnit *mem, size_t size) { long i = 0, count = 0; void *p = NULL; size += sizeof(long); //留一个long的位置记录该块的容量 //寻找合适的空块 for (i = 0; i < mem->size; i++) { if (*(unsigned char *)(mem->fileMapAddress + i) == 0) { count++; if (count == size) { p = mem->fileMapAddress + (i + 1 - size); //找到足够大小的块,减去刚刚遍历越过的位置得到起始地址 *(long *)p = size; //记录真正大小 p += sizeof(long); //掩盖块大小记录标记 break; } } else { i += *(long *)(mem->fileMapAddress + i) - 1; //每一个块的头部都是占用空间量,所以直接跳过。块与块之间的空位如果足够使用的话会形成新的块 count = 0; } } return p; }/* 已分配空间大小统计 @param p 虚拟内存条分配的连续空间地址 @return 占用虚拟内存条的空间 */ int fileMapObjectSize(void *p) { long size = *(long *)(p - sizeof(long)); printf("This fileMapObject's size is %ld\n", size); return size; }/* 销毁已分配空间 @param p 虚拟内存条分配的连续空间地址 @return 是否成功 */ int fileMapFree(void *p) { memset(p - sizeof(long), 0, fileMapObjectSize(p)); return 1; }int destroyVirtualMemory(MemoryUnit *m) { munmap(m->fileMapAddress, m->size); close(m->fileHandle); free(m); return 1; }

编写测试程序:
int main() { MemoryUnit *m = createVirtualMemory("./shit.txt", 1024 * 1024 * 300); //测试1 int *p = (int *)fileMapMalloc(m, sizeof(int) * 5); p[0] = 1; p[1] = 999; p[4] = 0xFFFFFFFF; int i; for (i = 0; i < 5; i++) { printf("%d\n", p[i]); } //测试2 char *p2 = (char *)fileMapMalloc(m, sizeof(char) * 6); strcpy(p2, "hello"); printf("%s\n", p2); //释放测试 for (int i = 0; i < 60; i++) { printf("%d ", *(unsigned char *)(m->fileMapAddress + i)); } printf("\n"); fileMapFree(p); //测试3 p = (int *)fileMapMalloc(m, sizeof(int) * 3); p[0] = 1; p[1] = 999; p[2] = 0xFFFFFFFF; for (i = 0; i < 3; i++) { printf("%d\n", p[i]); } p = fileMapMalloc(m, 3); memset(p, 255, 3); for (int i = 0; i < 60; i++) { printf("%d ", *(unsigned char *)(m->fileMapAddress + i)); } printf("\n"); //释放测试2 fileMapFree(p2); for (int i = 0; i < 60; i++) { printf("%d ", *(unsigned char *)(m->fileMapAddress + i)); } printf("\n"); destroyVirtualMemory(m); }

测试结果:
create file ./shit.txt successed!
open file ./shit.txt successed!
1
999
0
0
-1
hello
28 0 0 0 0 0 0 0 1 0 0 0 231 3 0 0 0 0 0 0 0 0 0 0 255 255 255 255 14 0 0 0 0 0 0 0 104 101 108 108 111 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
This fileMapObject's size is 28
1
999
-1
20 0 0 0 0 0 0 0 1 0 0 0 231 3 0 0 255 255 255 255 0 0 0 0 0 0 0 0 14 0 0 0 0 0 0 0 104 101 108 108 111 0 11 0 0 0 0 0 0 0 255 255 255 0 0 0 0 0 0 0
This fileMapObject's size is 14
20 0 0 0 0 0 0 0 1 0 0 0 231 3 0 0 255 255 255 255 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11 0 0 0 0 0 0 0 255 255 255 0 0 0 0 0 0 0
【C语言|一种基于linux mmap特性的应用层虚拟内存工具的编写】可以看到虚拟内存的使用情况符合规划的情况,实验成功。

    推荐阅读