📊 Linux C++ 内存映射文件:从原理到实践

🎯 概述与背景

文件映射(Memory-mapped Files)是现代操作系统提供的一种高效文件I/O机制,它将磁盘文件直接映射到进程的虚拟地址空间,使得应用程序可以像访问内存一样访问文件内容。这种技术在大数据处理、数据库系统、高性能计算等领域发挥着重要作用。

传统的文件I/O操作涉及多次数据拷贝:从磁盘到内核缓冲区,再从内核缓冲区到用户空间。而内存映射文件通过虚拟内存管理机制,实现了零拷贝的数据访问,显著提升了I/O性能。

应用程序
虚拟地址空间
页表
物理内存
磁盘文件
操作系统

🏗️ 架构设计分析

🔧 类层次结构设计

FileMapping类采用RAII(Resource Acquisition Is Initialization)设计模式,确保资源的安全管理。类的核心设计思想包括:

🚀 资源管理自动化

  • 通过析构函数自动释放系统资源,避免内存泄漏和文件描述符泄漏
  • 使用移动语义支持资源的有效转移,禁止拷贝构造防止重复释放

🛡️ 异常安全保证

  • 所有可能失败的操作都抛出异常,确保程序的健壮性
  • 构造函数不执行可能失败的操作,将初始化延迟到Open()方法

📋 核心成员变量分析

FileMapping
-int file_descriptor_
-std::string file_name_
-Mode mode_
-size_t file_size_
-void* mapped_data_
-bool is_open_
+Open()
+Close()
+Data()
+Sync() : bool
+Write() : bool
+Read() : bool

关键成员说明:

  • file_descriptor_:底层文件描述符,用于文件操作
  • mapped_data_:内存映射区域的起始地址
  • file_size_:映射文件的大小,用于边界检查
  • mode_:映射模式,控制访问权限和共享特性

⚙️ 核心技术原理

🎪 内存映射机制

内存映射文件的核心原理是利用操作系统的虚拟内存管理机制,将文件内容直接映射到进程的地址空间。当进程访问映射区域时,操作系统自动处理页故障并将对应的文件数据加载到物理内存。

🔬 映射过程详解:

应用程序 FileMapping 操作系统 文件系统 Open() open() + fstat() 打开文件获取描述符 mmap() 建立内存映射 返回映射地址 映射完成 读写操作 直接内存访问 按需加载/写回 应用程序 FileMapping 操作系统 文件系统

🛡️ 三种映射模式对比

模式 访问权限 共享特性 适用场景
ReadOnly 只读 共享映射 配置文件读取、日志分析
ReadWrite 读写 共享映射 数据库文件、实时数据处理
CopyOnWrite 读写 私有映射 进程快照、数据备份

🎨 设计模式与编程范式

📐 RAII模式实现

RAII模式是C++资源管理的核心思想,FileMapping类完美体现了这一理念:

// 构造函数:资源获取
FileMapping(const std::string& file_name, Mode mode = Mode::ReadOnly)

// 析构函数:资源释放
~FileMapping() { Close(); }

// 移动语义:资源转移
FileMapping(FileMapping&& other) noexcept

🔄 移动语义支持

通过实现移动构造函数和移动赋值运算符,类支持高效的资源转移:

FileMapping(FileMapping&& other) noexcept
    : file_descriptor_(other.file_descriptor_),
      file_name_(std::move(other.file_name_)),
      // ... 其他成员初始化
{
    other.Reset();  // 源对象状态重置
}

🧩 模板元编程应用

类中使用模板方法和类型特征检查,确保类型安全:

template<typename T>
T* Data() noexcept 
{
    static_assert(std::is_trivial_v<T>, "T must be trivial type");
    return IsOpen() ? reinterpret_cast<T*>(mapped_data_) : NULL;
}

⚡ 优化策略

🚀 零拷贝优势

与传统文件I/O相比,内存映射文件具有显著的性能优势:

传统I/O流程:

  1. 读取请求到内核
  2. 内核从磁盘读取数据到内核缓冲区
  3. 数据从内核缓冲区拷贝到用户空间

内存映射流程:

  1. 建立内存映射
  2. 直接访问虚拟内存地址
  3. 操作系统按需加载数据页

📊 性能对比

传统read
2次拷贝
内存映射
零拷贝
性能提升
30%-50%

🔧 同步机制优化

类提供了灵活的同步策略,平衡性能和数据一致性:

bool Sync(bool async = false) 
{
    int flags = async ? MS_ASYNC : MS_SYNC;
    return msync(mapped_data_, file_size_, flags) == 0;
}

🛠️ 安全性与健壮性

🎯 边界检查机制

类实现了完善的边界检查,防止内存越界访问:

bool WriteBytes(size_t offset, const void* source, size_t count) 
{
    if (!IsOpen() || mode_ == Mode::ReadOnly) return false;
    if (offset >= file_size_) return false;           // 偏移量检查
    if (offset + count > file_size_) return false;  // 长度检查
    if (!source) return false;                        // 空指针检查
    
    std::memcpy(static_cast<char*>(mapped_data_) + offset, source, count);
    return true;
}

🔒 异常安全保证

采用异常安全编程风格,确保资源在任何情况下都能正确释放:

void OpenFile() 
{
    file_descriptor_ = open(file_name_.c_str(), open_flags);
    if (file_descriptor_ == -1) 
    {
        throw std::system_error(errno, std::system_category(), 
                               "Failed to open file: " + file_name_);
    }
    // ... 其他操作
}

📈 应用场景

🗄️ 数据库系统

内存映射文件在数据库系统中广泛应用,如SQLite、LevelDB等都使用mmap来管理数据文件:

优势体现:

  • 高效的随机访问性能
  • 自动的缓存管理
  • 简化的并发控制

📊 大数据处理

在大数据场景下,内存映射文件能够处理远超物理内存大小的数据文件:

// 处理大型数据文件的示例
FileMapping mapping("huge_dataset.bin", FileMapping::Mode::ReadOnly);
mapping.Open();

// 即使文件大小为几十GB,也能高效访问
for (size_t offset = 0; offset < mapping.Size(); offset += chunk_size) 
{
    ProcessChunk(mapping.Data<char>() + offset, chunk_size);
}

🎮 游戏开发

游戏资源文件通常使用内存映射进行快速加载:

  • 纹理、模型等资源的快速加载
  • 关卡数据的流式加载
  • 存档文件的快速读写

🔍 测试框架分析

🧪 全面的测试覆盖

提供的测试代码实现了多层次的测试覆盖:

测试类型统计:

  • ✅ 基本功能测试:映射创建、文件访问
  • ✅ 模式测试:读写模式、写时复制
  • ✅ 边界测试:越界访问、空指针处理
  • ✅ 性能测试:大文件处理
  • ✅ 异常测试:错误条件处理

📋 测试架构设计

TestFileManager
测试文件管理
文件创建
文件清理
测试用例
基本功能
边界检查
性能测试
异常处理
TestBasicFunctionality
TestBoundaryChecks
TestLargeFile
TestErrorConditions

🎯 测试亮点

  1. 隔离性:每个测试用例使用独立的测试文件
  2. 自动化:测试文件自动创建和清理
  3. 全面性:覆盖正常路径和异常路径
  4. 可重复性:使用随机文件名避免冲突

💡 实践建议

✅ 使用时机判断

适合使用内存映射的场景:

  • 需要频繁随机访问大文件
  • 文件大小相对稳定
  • 需要与其他进程共享数据
  • 性能要求极高的I/O操作

不适合使用的场景:

  • 文件频繁扩展或收缩
  • 需要精细的缓存控制
  • 内存资源极度紧张

🔧 配置优化建议

  1. 页面大小对齐:确保访问按页面大小对齐以获得最佳性能
  2. 预读策略:对顺序访问模式启用预读
  3. 同步策略:根据数据重要性选择合适的同步频率

⚠️ 注意事项

  1. 资源限制:映射区域受虚拟地址空间限制
  2. 错误处理:mmap失败可能返回MAP_FAILED而非NULL
  3. 可移植性:不同系统对mmap参数的支持有差异

🎉 源代码

✨ HPP头文件

#ifndef FILE_MAPPING_HPP
#define FILE_MAPPING_HPP

#include <string>
#include <system_error>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <stdexcept>
#include <cstring>
#include <type_traits>
#include <algorithm>
#include <cerrno>

class FileMapping 
{
public:
    // 文件映射模式枚举
    enum class Mode 
    {
        ReadOnly,    // 只读模式
        ReadWrite,   // 读写模式  
        CopyOnWrite  // 写时复制模式
    };

    // 构造函数:仅初始化成员变量,不执行可能失败的操作
    FileMapping(const std::string& file_name, Mode mode = Mode::ReadOnly)
        : file_name_(file_name), mode_(mode), file_size_(0), mapped_data_(NULL), file_descriptor_(-1), is_open_(false) 
    {
        // 构造函数不执行任何可能抛出异常的操作
    }

    // 禁止拷贝构造
    FileMapping(const FileMapping&) = delete;
    FileMapping&                                    operator=(const FileMapping&) = delete;

    // 移动构造函数
    FileMapping(FileMapping&& other) noexcept
        : file_descriptor_(other.file_descriptor_), file_name_(std::move(other.file_name_)),
          mode_(other.mode_), file_size_(other.file_size_), mapped_data_(other.mapped_data_), is_open_(other.is_open_) 
    {
        other.Reset();  // 重置原对象状态
    }

    // 移动赋值运算符
    FileMapping&                                    operator=(FileMapping&& other) noexcept 
    {
        if (this != &other) 
        {
            Close();  // 关闭当前映射
            file_descriptor_ = other.file_descriptor_;
            file_name_ = std::move(other.file_name_);
            mode_ = other.mode_;
            file_size_ = other.file_size_;
            mapped_data_ = other.mapped_data_;
            is_open_ = other.is_open_;
            other.Reset();  // 重置原对象状态
        }
        return *this;
    }

    // 析构函数:自动清理资源
    ~FileMapping() 
    {
        Close();
    }

    // 打开文件并建立内存映射
    void                                            Open() 
    {
        if (is_open_) 
        {
            Close();  // 如果已经打开,先关闭
        }
        
        OpenFile();  // 打开文件
        MapFile();   // 映射文件到内存
        is_open_ = true;
    }

    // 关闭文件映射
    void                                            Close() noexcept 
    {
        if (mapped_data_ != NULL && mapped_data_ != MAP_FAILED) 
        {
            munmap(mapped_data_, file_size_);  // 解除内存映射
        }

        if (file_descriptor_ != -1) 
        {
            close(file_descriptor_);  // 关闭文件描述符
        }

        Reset();
    }

    // 获取映射数据的模板方法(非常量版本)
    template<typename T>
    T*                                              Data() noexcept 
    {
        static_assert(std::is_trivial_v<T>, "T must be trivial type");  // 检查类型是否为平凡类型
        return IsOpen() ? reinterpret_cast<T*>(mapped_data_) : NULL;
    }

    // 获取映射数据的模板方法(常量版本)
    template<typename T>
    const T*                                        Data() const noexcept 
    {
        static_assert(std::is_trivial_v<T>, "T must be trivial type");  // 检查类型是否为平凡类型
        return IsOpen() ? reinterpret_cast<const T*>(mapped_data_) : NULL;
    }

    // 获取原始指针的方法
    void*                                           Data() noexcept { return IsOpen() ? mapped_data_ : NULL; }
    const void*                                     Data() const noexcept { return IsOpen() ? mapped_data_ : NULL; }
    const void*                                     CData() const noexcept { return IsOpen() ? mapped_data_ : NULL; }

    // 获取文件大小
    size_t                                          Size() const noexcept { return file_size_; }

    // 获取文件名
    const std::string&                              FileName() const noexcept { return file_name_; }

    // 获取映射模式
    Mode                                            GetMode() const noexcept { return mode_; }

    // 检查是否成功打开
    bool                                            IsOpen() const noexcept 
    { 
        return is_open_ && mapped_data_ != NULL && mapped_data_ != MAP_FAILED && file_size_ > 0;
    }

    // 同步内存映射到磁盘
    bool                                            Sync(bool async = false) 
    {
        if (!IsOpen() || mode_ == Mode::ReadOnly) 
        {
            return false;  // 只读模式无法同步
        }

        int flags = async ? MS_ASYNC : MS_SYNC;  // 选择同步模式
        return msync(mapped_data_, file_size_, flags) == 0;
    }

    // 写入指定类型的数据
    template<typename T>
    bool                                            Write(size_t offset, const T& value) 
    {
        static_assert(std::is_trivial_v<T>, "T must be trivial type");  // 检查类型是否为平凡类型

        return WriteBytes(offset, &value, sizeof(T));
    }

    // 读取指定类型的数据
    template<typename T>
    bool                                            Read(size_t offset, T& value) const 
    {
        static_assert(std::is_trivial_v<T>, "T must be trivial type");  // 检查类型是否为平凡类型
        
        return ReadBytes(offset, &value, sizeof(T));
    }

    // 写入字节数据
    bool                                            WriteBytes(size_t offset, const void* source, size_t count) 
    {
        if (!IsOpen() || mode_ == Mode::ReadOnly) return false;  // 只读模式禁止写入
        if (offset >= file_size_) return false;  // 偏移量越界检查
        if (count == 0) return true;  // 写入0字节直接返回成功
        if (offset + count > file_size_) return false;  // 写入长度越界检查
        if (!source) return false;  // 空指针检查
        
        std::memcpy(static_cast<char*>(mapped_data_) + offset, source, count);  // 执行内存拷贝
        return true;
    }

    // 读取字节数据
    bool                                            ReadBytes(size_t offset, void* destination, size_t count) const 
    {
        if (!IsOpen()) return false;  // 打开状态检查
        if (offset >= file_size_) return false;  // 偏移量越界检查
        if (count == 0) return true;  // 读取0字节直接返回成功
        if (offset + count > file_size_) return false;  // 读取长度越界检查
        if (!destination) return false;  // 空指针检查
        
        std::memcpy(destination, static_cast<const char*>(mapped_data_) + offset, count);  // 执行内存拷贝
        return true;
    }

    // 写入字符串数据
    bool                                            WriteString(size_t offset, const std::string& str, bool null_terminate = false) 
    {
        if (!IsOpen() || mode_ == Mode::ReadOnly) 
        {
            return false;  // 只读模式禁止写入
        }
        
        size_t write_size = str.size();  // 计算写入大小
        if (null_terminate) 
        {
            write_size += 1;  // 包含空终止符
        }

        if (offset + write_size > file_size_) 
        {
            return false;  // 空间不足检查
        }
        
        if (!WriteBytes(offset, str.c_str(), str.size())) 
        {
            return false;  // 写入字符串内容
        }
        
        if (null_terminate) 
        {
            char null_char = '\0';
            return WriteBytes(offset + str.size(), &null_char, 1);  // 写入空终止符
        }

        return true;
    }

    // 读取字符串数据
    std::string                                     ReadString(size_t offset, size_t max_length) const 
    {
        if (!IsOpen() || offset >= file_size_) return "";  // 基本参数检查
        
        const char* start = static_cast<const char*>(mapped_data_) + offset;  // 起始位置
        size_t remaining = file_size_ - offset;  // 剩余可用空间
        size_t length = std::min(max_length, remaining);  // 计算实际读取长度
        
        const char* end = static_cast<const char*>(std::memchr(start, '\0', length));  // 查找空终止符
        if (end) 
        {
            length = end - start;  // 找到空终止符,调整长度
        } 
        else 
        {
            length = std::min(length, remaining);  // 未找到,使用最大可用长度
        }
        
        return std::string(start, length);  // 构造并返回字符串
    }

private:
    // 打开文件并获取文件信息
    void                                            OpenFile() 
    {
        int open_flags = O_CLOEXEC;  // 设置文件打开标志
        switch(mode_) 
        {
            case Mode::ReadOnly:
                open_flags |= O_RDONLY;  // 只读模式
                break;
            case Mode::ReadWrite:
                open_flags |= O_RDWR;  // 读写模式
                break;
            case Mode::CopyOnWrite:
                open_flags |= O_RDONLY;  // 写时复制模式
                break;
        }

        file_descriptor_ = open(file_name_.c_str(), open_flags);  // 打开文件
        if (file_descriptor_ == -1) 
        {
            throw std::system_error(errno, std::system_category(), 
                                   "Failed to open file: " + file_name_);  // 打开失败抛出异常
        }

        struct stat file_stat;
        if (fstat(file_descriptor_, &file_stat) == -1) 
        {
            auto saved_errno = errno;
            close(file_descriptor_);

            file_descriptor_ = -1;
            throw std::system_error(saved_errno, std::system_category(), 
                                   "Failed to get file size");  // 获取文件大小失败
        }
        
        file_size_ = static_cast<size_t>(file_stat.st_size);  // 保存文件大小
        if (file_size_ == 0) {
            close(file_descriptor_);
            file_descriptor_ = -1;

            throw std::runtime_error("File size is zero");  // 文件为空抛出异常
        }
    }

    // 将文件映射到内存
    void                                            MapFile() 
    {
        int protection = PROT_READ;  // 设置内存保护标志
        int flags = MAP_SHARED;  // 设置映射标志
        
        switch(mode_) 
        {
            case Mode::ReadWrite:
                protection |= PROT_WRITE;  // 添加写权限
                break;
            case Mode::CopyOnWrite:
                protection |= PROT_WRITE;  // 添加写权限
                flags = MAP_PRIVATE;  // 私有映射
                break;
            default:
                break;
        }

        mapped_data_ = mmap(NULL, file_size_, protection, flags, file_descriptor_, 0);  // 执行内存映射
        if (mapped_data_ == MAP_FAILED) 
        {
            auto saved_errno = errno;
            close(file_descriptor_);

            file_descriptor_ = -1;
            mapped_data_ = NULL;
            throw std::system_error(saved_errno, std::system_category(), 
                                   "Memory mapping failed");  // 映射失败抛出异常
        }
    }

    // 重置对象状态
    void                                            Reset() noexcept 
    {
        file_descriptor_ = -1;
        mapped_data_ = NULL;
        file_size_ = 0;
        is_open_ = false;
    }

    int                                             file_descriptor_;   // 文件描述符
    std::string                                     file_name_;         // 文件名
    Mode                                            mode_;              // 映射模式
    size_t                                          file_size_;         // 文件大小
    void*                                           mapped_data_;       // 映射内存地址
    bool                                            is_open_;           // 是否已打开
};

#endif

📚 MAIN.CPP 测试例程

#include "file_mapping.hpp"
#include <iostream>
#include <fstream>
#include <vector>
#include <random>
#include <cassert>
#include <filesystem>
#include <cstdio>
#include <cstdlib>
#include <cstring>

// 测试文件管理类
class TestFileManager {
public:
    // 创建测试文件
    static std::string CreateTestFile(const std::string& content) {
        std::random_device random_device;
        std::string file_name = "test_file_" + std::to_string(random_device()) + ".dat";  // 生成随机文件名
        std::ofstream file(file_name, std::ios::binary);
        if (!file) throw std::runtime_error("无法创建测试文件: " + file_name);
        file.write(content.data(), content.size());  // 写入内容
        file.close();
        return file_name;
    }
    
    // 删除测试文件
    static void RemoveTestFile(const std::string& file_name) {
        std::error_code error_code;
        if (std::filesystem::exists(file_name, error_code)) {
            std::remove(file_name.c_str());  // 删除文件
        }
    }
    
    // 创建大文件
    static std::string CreateLargeFile(size_t size, char fill_char = 'A') {
        std::random_device random_device;
        std::string file_name = "large_test_" + std::to_string(random_device()) + ".dat";  // 生成随机文件名
        std::ofstream file(file_name, std::ios::binary);
        if (!file) throw std::runtime_error("无法创建大文件: " + file_name);
        
        const size_t buffer_size = 1024 * 1024;  // 缓冲区大小1MB
        std::vector<char> buffer(std::min(size, buffer_size), fill_char);  // 创建填充缓冲区
        
        size_t remaining = size;
        while (remaining > 0) {
            size_t write_size = std::min(remaining, buffer_size);  // 计算本次写入大小
            file.write(buffer.data(), write_size);  // 写入文件
            remaining -= write_size;
        }
        file.close();
        return file_name;
    }
};

// 测试基本功能
void TestBasicFunctionality() {
    std::cout << "=== 测试基本功能 ===\n";
    
    auto content = "Hello, Memory Mapping World!";
    auto file_name = TestFileManager::CreateTestFile(content);
    
    try {
        FileMapping mapping(file_name, FileMapping::Mode::ReadOnly);
        mapping.Open();  // 显式打开文件映射
        
        if (mapping.Size() != std::strlen(content)) {
            std::cout << "✗ 文件大小不正确\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        if (!mapping.IsOpen()) {
            std::cout << "✗ 打开状态不正确\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        if (std::memcmp(mapping.Data(), content, mapping.Size()) != 0) {
            std::cout << "✗ 文件内容不匹配\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        std::cout << "✓ 基本功能测试通过\n";
    } catch (const std::exception& exception) {
        TestFileManager::RemoveTestFile(file_name);
        std::cout << "✗ 异常: " << exception.what() << "\n";
        return;
    }
    
    TestFileManager::RemoveTestFile(file_name);
}

// 测试读写模式
void TestReadWriteMode() {
    std::cout << "=== 测试读写模式 ===\n";
    
    std::string initial_content(50, 'A');
    auto file_name = TestFileManager::CreateTestFile(initial_content);
    
    try {
        FileMapping mapping(file_name, FileMapping::Mode::ReadWrite);
        mapping.Open();  // 显式打开文件映射
        
        std::string new_content = "Modified Content";
        if (new_content.size() > mapping.Size()) {
            std::cout << "⚠ 新内容比原文件长,无法写入完整内容\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        if (!mapping.WriteBytes(0, new_content.c_str(), new_content.size())) {
            std::cout << "✗ 写入操作失败\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        if (!mapping.Sync()) {
            std::cout << "✗ 同步操作失败\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        char buffer[100] = {0};
        if (!mapping.ReadBytes(0, buffer, new_content.size())) {
            std::cout << "✗ 读取操作失败\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        if (std::strncmp(buffer, new_content.c_str(), new_content.size()) != 0) {
            std::cout << "✗ 内容验证失败\n";
            std::cout << "期望: " << new_content << "\n";
            std::cout << "实际: " << buffer << "\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        std::cout << "✓ 读写模式测试通过\n";
    } catch (const std::exception& exception) {
        TestFileManager::RemoveTestFile(file_name);
        std::cout << "✗ 异常: " << exception.what() << "\n";
        return;
    }
    
    TestFileManager::RemoveTestFile(file_name);
}

// 测试写时复制模式
void TestCopyOnWrite() {
    std::cout << "=== 测试写时复制 ===\n";
    
    auto content = "Original Data";
    auto file_name = TestFileManager::CreateTestFile(content);
    
    try {
        FileMapping mapping(file_name, FileMapping::Mode::CopyOnWrite);
        mapping.Open();  // 显式打开文件映射
        
        const char* modified_content = "Modified Data";
        if (!mapping.WriteBytes(0, modified_content, std::strlen(modified_content))) {
            std::cout << "✗ 写时复制写入失败\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        char buffer[100] = {0};
        if (!mapping.ReadBytes(0, buffer, std::strlen(modified_content))) {
            std::cout << "✗ 写时复制读取失败\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        if (std::strncmp(buffer, modified_content, std::strlen(modified_content)) != 0) {
            std::cout << "✗ 写时复制内容验证失败\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        std::cout << "✓ 写时复制测试通过\n";
    } catch (const std::exception& exception) {
        TestFileManager::RemoveTestFile(file_name);
        std::cout << "✗ 异常: " << exception.what() << "\n";
        return;
    }
    
    TestFileManager::RemoveTestFile(file_name);
}

// 测试边界检查
void TestBoundaryChecks() {
    std::cout << "=== 测试边界检查 ===\n";
    
    auto content = "Test Data";
    auto file_name = TestFileManager::CreateTestFile(content);
    
    try {
        FileMapping mapping(file_name, FileMapping::Mode::ReadOnly);
        mapping.Open();  // 显式打开文件映射
        
        char buffer[100];
        if (mapping.ReadBytes(mapping.Size() + 1, buffer, 1)) {
            std::cout << "✗ 边界检查失败:超出范围读取应该失败\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        if (mapping.ReadBytes(0, buffer, mapping.Size() + 100)) {
            std::cout << "✗ 边界检查失败:超出大小读取应该失败\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        FileMapping read_write_mapping(file_name, FileMapping::Mode::ReadWrite);
        read_write_mapping.Open();
        if (read_write_mapping.WriteBytes(mapping.Size() + 1, "X", 1)) {
            std::cout << "✗ 边界检查失败:超出范围写入应该失败\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        if (read_write_mapping.WriteBytes(0, "Too Long Data", mapping.Size() + 10)) {
            std::cout << "✗ 边界检查失败:超出大小写入应该失败\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        if (mapping.ReadBytes(0, NULL, 10)) {
            std::cout << "✗ 边界检查失败:空指针读取应该失败\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        if (read_write_mapping.WriteBytes(0, NULL, 10)) {
            std::cout << "✗ 边界检查失败:空指针写入应该失败\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        std::cout << "✓ 边界检查测试通过\n";
    } catch (const std::exception& exception) {
        TestFileManager::RemoveTestFile(file_name);
        std::cout << "✗ 异常: " << exception.what() << "\n";
        return;
    }
    
    TestFileManager::RemoveTestFile(file_name);
}

// 测试大文件处理
void TestLargeFile() {
    std::cout << "=== 测试大文件 ===\n";
    
    const size_t large_size = 2 * 1024 * 1024;  // 2MB大文件
    auto file_name = TestFileManager::CreateLargeFile(large_size, 'X');
    
    try {
        FileMapping mapping(file_name, FileMapping::Mode::ReadOnly);
        mapping.Open();  // 显式打开文件映射
        
        if (mapping.Size() != large_size) {
            std::cout << "✗ 大文件大小不正确\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        char test_char;
        for (size_t i = 0; i < large_size; i += 512 * 1024) {  // 每隔512KB采样检查
            if (!mapping.ReadBytes(i, &test_char, 1)) {
                std::cout << "✗ 大文件读取失败 at offset " << i << "\n";
                TestFileManager::RemoveTestFile(file_name);
                return;
            }
            if (test_char != 'X') {
                std::cout << "✗ 大文件内容验证失败\n";
                TestFileManager::RemoveTestFile(file_name);
                return;
            }
        }
        
        std::cout << "✓ 大文件测试通过\n";
    } catch (const std::exception& exception) {
        TestFileManager::RemoveTestFile(file_name);
        std::cout << "✗ 异常: " << exception.what() << "\n";
        return;
    }
    
    TestFileManager::RemoveTestFile(file_name);
}

// 测试移动语义
void TestMoveSemantics() {
    std::cout << "=== 测试移动语义 ===\n";
    
    auto content = "Move Test";
    auto file_name = TestFileManager::CreateTestFile(content);
    
    try {
        FileMapping mapping1(file_name, FileMapping::Mode::ReadOnly);
        mapping1.Open();  // 显式打开文件映射
        FileMapping mapping2 = std::move(mapping1);  // 移动构造
        
        if (mapping1.IsOpen()) {
            std::cout << "✗ 移动后原对象应该无效\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        if (!mapping2.IsOpen()) {
            std::cout << "✗ 移动后新对象应该有效\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        if (mapping2.Size() != std::strlen(content)) {
            std::cout << "✗ 移动后大小不正确\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        std::cout << "✓ 移动语义测试通过\n";
    } catch (const std::exception& exception) {
        TestFileManager::RemoveTestFile(file_name);
        std::cout << "✗ 异常: " << exception.what() << "\n";
        return;
    }
    
    TestFileManager::RemoveTestFile(file_name);
}

// 测试模板方法
void TestTemplateMethods() {
    std::cout << "=== 测试模板方法 ===\n";
    
    struct TestStruct {
        int integer_field;
        double double_field;
        char char_array[10];
    };
    
    TestStruct original{42, 3.14, "test"};
    
    std::string file_content(reinterpret_cast<const char*>(&original), sizeof(original));
    auto file_name = TestFileManager::CreateTestFile(file_content);
    
    try {
        FileMapping mapping(file_name, FileMapping::Mode::ReadWrite);
        mapping.Open();  // 显式打开文件映射
        
        TestStruct read_value;
        if (!mapping.Read(0, read_value)) {
            std::cout << "✗ 模板读取失败\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        if (original.integer_field != read_value.integer_field || 
            original.double_field != read_value.double_field || 
            std::strcmp(original.char_array, read_value.char_array) != 0) {
            std::cout << "✗ 模板读取内容不匹配\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        TestStruct new_value{100, 2.71, "modified"};
        if (!mapping.Write(0, new_value)) {
            std::cout << "✗ 模板写入失败\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        std::cout << "✓ 模板方法测试通过\n";
    } catch (const std::exception& exception) {
        TestFileManager::RemoveTestFile(file_name);
        std::cout << "✗ 异常: " << exception.what() << "\n";
        return;
    }
    
    TestFileManager::RemoveTestFile(file_name);
}

// 测试字符串方法
void TestStringMethods() {
    std::cout << "=== 测试字符串方法 ===\n";
    
    auto content = "Hello World";
    auto file_name = TestFileManager::CreateTestFile(content);
    
    try {
        FileMapping mapping(file_name, FileMapping::Mode::ReadWrite);
        mapping.Open();  // 显式打开文件映射
        
        std::string new_string = "Modified";
        if (!mapping.WriteString(0, new_string, true)) {
            std::cout << "✗ 字符串写入失败\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        std::string read_string = mapping.ReadString(0, 100);
        if (read_string != new_string) {
            std::cout << "✗ 字符串读取不匹配\n";
            std::cout << "期望: " << new_string << "\n";
            std::cout << "实际: " << read_string << "\n";
            TestFileManager::RemoveTestFile(file_name);
            return;
        }
        
        std::cout << "✓ 字符串方法测试通过\n";
    } catch (const std::exception& exception) {
        TestFileManager::RemoveTestFile(file_name);
        std::cout << "✗ 异常: " << exception.what() << "\n";
        return;
    }
    
    TestFileManager::RemoveTestFile(file_name);
}

// 测试错误条件
void TestErrorConditions() {
    std::cout << "=== 测试错误条件 ===\n";
    
    try {
        FileMapping mapping("nonexistent_file.xyz", FileMapping::Mode::ReadOnly);
        mapping.Open();  // 显式打开文件映射
        std::cout << "✗ 文件不存在应该抛出异常\n";
    } catch (const std::system_error&) {
        std::cout << "✓ 文件不存在异常测试通过\n";
    } catch (...) {
        std::cout << "✗ 文件不存在应该抛出system_error\n";
    }
    
    auto empty_file_name = TestFileManager::CreateTestFile("");
    try {
        FileMapping mapping(empty_file_name, FileMapping::Mode::ReadOnly);
        mapping.Open();  // 显式打开文件映射
        std::cout << "✗ 空文件应该抛出异常\n";
    } catch (const std::runtime_error&) {
        std::cout << "✓ 空文件异常测试通过\n";
    } catch (...) {
        std::cout << "✗ 空文件应该抛出runtime_error\n";
    }
    TestFileManager::RemoveTestFile(empty_file_name);
}

// 测试资源管理
void TestResourceManagement() {
    std::cout << "=== 测试资源管理 ===\n";
    
    auto content = "Resource Test";
    auto file_name = TestFileManager::CreateTestFile(content);
    
    try {
        {
            FileMapping mapping(file_name, FileMapping::Mode::ReadOnly);
            mapping.Open();  // 显式打开文件映射
            if (!mapping.IsOpen()) {
                std::cout << "✗ 资源管理测试:文件应该成功打开\n";
                TestFileManager::RemoveTestFile(file_name);
                return;
            }
        }  // 作用域结束,应该自动释放资源
        
        // 重新打开验证资源已释放
        FileMapping mapping2(file_name, FileMapping::Mode::ReadOnly);
        mapping2.Open();  // 应该能正常打开
        
        std::cout << "✓ 资源管理测试通过\n";
    } catch (const std::exception& exception) {
        TestFileManager::RemoveTestFile(file_name);
        std::cout << "✗ 异常: " << exception.what() << "\n";
        return;
    }
    
    TestFileManager::RemoveTestFile(file_name);
}

// 主测试函数
int main() {
    std::cout << "开始文件映射测试...\n\n";
    
    TestBasicFunctionality();      // 基本功能测试
    TestReadWriteMode();           // 读写模式测试
    TestCopyOnWrite();             // 写时复制测试
    TestBoundaryChecks();          // 边界检查测试
    TestLargeFile();               // 大文件测试
    TestMoveSemantics();           // 移动语义测试
    TestTemplateMethods();         // 模板方法测试
    TestStringMethods();           // 字符串方法测试
    TestErrorConditions();         // 错误条件测试
    TestResourceManagement();      // 资源管理测试
    
    std::cout << "\n测试完成!\n";
    return 0;
}

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐