C/C++ 实现内存映射

C/C++ 实现内存映射

权重采用内存映射方式加载,避免了大规模内存拷贝,特别适合大模型部署。

在C语言中实现内存映射主要使用mmap()系统调用。以下是一个完整的示例,展示如何使用内存映射进行文件读写:

  1. 完整的内存映射工具函数
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

typedef struct {
    void *addr;      // 映射地址
    size_t size;     // 映射大小
    int fd;          // 文件描述符
} MappedFile;

// 打开并映射文件
MappedFile* map_file(const char *filename, size_t size, int create) {
    MappedFile *mf = malloc(sizeof(MappedFile));
    if (!mf) return NULL;
    
    int flags = O_RDWR;
    if (create) {
        flags |= O_CREAT;
    }
    
    mf->fd = open(filename, flags, 0644);
    if (mf->fd == -1) {
        perror("open");
        free(mf);
        return NULL;
    }
    
    // 获取文件大小(如果不指定大小)
    if (size == 0) {
        struct stat st;
        if (fstat(mf->fd, &st) == -1) {
            perror("fstat");
            close(mf->fd);
            free(mf);
            return NULL;
        }
        size = st.st_size;
    }
    
    // 如果是创建模式,调整文件大小
    if (create && ftruncate(mf->fd, size) == -1) {
        perror("ftruncate");
        close(mf->fd);
        free(mf);
        return NULL;
    }
    
    mf->size = size;
    
    // 创建映射
    mf->addr = mmap(NULL, size, PROT_READ | PROT_WRITE, 
                   MAP_SHARED, mf->fd, 0);
    
    if (mf->addr == MAP_FAILED) {
        perror("mmap");
        close(mf->fd);
        free(mf);
        return NULL;
    }
    
    printf("成功映射文件: %s, 大小: %.2f MB\n", 
           filename, size / (1024.0 * 1024.0));
    
    return mf;
}

// 同步映射到磁盘
int sync_mapping(MappedFile *mf) {
    if (msync(mf->addr, mf->size, MS_SYNC) == -1) {
        perror("msync");
        return -1;
    }
    return 0;
}

// 取消映射并关闭
int unmap_file(MappedFile *mf) {
    if (munmap(mf->addr, mf->size) == -1) {
        perror("munmap");
        return -1;
    }
    
    if (close(mf->fd) == -1) {
        perror("close");
        return -1;
    }
    
    free(mf);
    return 0;
}

// 使用示例 - 创建6GB的大文件
int main() {
    // 创建新文件并映射 - 使用明确的类型转换避免警告
    size_t file_size = (size_t)6 * 1024 * 1024 * 1024;  // 6GB
    printf("尝试创建 %.2f GB 的文件...\n", file_size / (1024.0 * 1024.0 * 1024.0));
    
    MappedFile *mf = map_file("large_data.bin", file_size, 1);
    if (!mf) {
        fprintf(stderr, "映射文件失败\n");
        return 1;
    }
    
    // 写入数据到文件开头
    char *data = (char *)mf->addr;
    sprintf(data, "这是一个 %.2f GB 的内存映射文件\n", 
            mf->size / (1024.0 * 1024.0 * 1024.0));
    
    // 在文件末尾也写入一些数据
    if (mf->size > 100) {
        char *end_data = (char *)mf->addr + mf->size - 100;
        sprintf(end_data, "文件末尾标记 - 文件大小: %zu 字节", mf->size);
    }
    
    // 同步到磁盘
    printf("同步数据到磁盘...\n");
    if (sync_mapping(mf) == -1) {
        fprintf(stderr, "同步失败\n");
    }
    
    // 读取并显示文件开头和结尾的数据
    printf("\n文件开头内容:\n%s", (char *)mf->addr);
    
    if (mf->size > 100) {
        printf("\n文件末尾内容:\n%s\n", (char *)mf->addr + mf->size - 100);
    }
    
    // 清理
    printf("清理资源...\n");
    if (unmap_file(mf) == -1) {
        fprintf(stderr, "取消映射失败\n");
    }
    
    printf("完成!\n");
    return 0;
}

  1. 完整的内存映射工具函数
# 编译
gcc -o mmap_example mmap_example.c

# 运行
./mmap_example

  1. 关键函数说明
mmap() - 创建内存映射
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

munmap() - 取消内存映射
int munmap(void *addr, size_t length);


msync() - 同步内存到磁盘
int msync(void *addr, size_t length, int flags);

  1. 注意事项
  • 权限设置:

    • PROT_READ - 可读
    • PROT_WRITE - 可写
    • PROT_EXEC - 可执行
    • PROT_NONE - 不可访问
  • 映射类型:

    • MAP_SHARED - 共享映射(修改会写回文件)
    • MAP_PRIVATE - 私有映射(写时复制)
    • MAP_ANONYMOUS - 匿名映射(不关联文件)
  • 错误处理:

    • 检查返回值是否为 MAP_FAILED
    • 使用 perror() 输出错误信息
  • 性能考虑:

  • 适合大文件操作

  • 减少系统调用次数

  • 注意内存对齐和页面大小

这个实现提供了内存映射的基本功能,可以根据具体需求进行扩展和优化。

posted @ 2025-12-10 10:10  michaelchengjl  阅读(21)  评论(0)    收藏  举报