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

为了方便一些对时间不敏感的大数据块的存放,尤其是在JNI环境中避免系统OOM,我打算给予MMAP做一套虚拟内存工具:

首先来想一下申请连续块时的各种情况,方便编写仿malloc函数,各种增删情况如图:

每个块开头用一个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 <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

/* 虚拟内存条
   @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 

可以看到虚拟内存的使用情况符合规划的情况,实验成功。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值