Linux C++ 内存映射文件:从原理到实践
Linux C++内存映射文件技术通过将磁盘文件映射到进程地址空间,实现高效零拷贝I/O。该技术采用RAII模式管理资源,支持三种映射模式(只读、读写、写时复制),利用虚拟内存机制提升性能30%-50%。核心类设计包含边界检查、异常安全和移动语义支持,适用于数据库、大数据处理等场景。测试框架覆盖功能、边界、性能和异常等多维度验证,确保系统健壮性。内存映射文件简化了大型文件处理,是高性能应用开发的重
📊 Linux C++ 内存映射文件:从原理到实践
🎯 概述与背景
文件映射(Memory-mapped Files)是现代操作系统提供的一种高效文件I/O机制,它将磁盘文件直接映射到进程的虚拟地址空间,使得应用程序可以像访问内存一样访问文件内容。这种技术在大数据处理、数据库系统、高性能计算等领域发挥着重要作用。
传统的文件I/O操作涉及多次数据拷贝:从磁盘到内核缓冲区,再从内核缓冲区到用户空间。而内存映射文件通过虚拟内存管理机制,实现了零拷贝的数据访问,显著提升了I/O性能。
🏗️ 架构设计分析
🔧 类层次结构设计
FileMapping类采用RAII(Resource Acquisition Is Initialization)设计模式,确保资源的安全管理。类的核心设计思想包括:
🚀 资源管理自动化
- 通过析构函数自动释放系统资源,避免内存泄漏和文件描述符泄漏
- 使用移动语义支持资源的有效转移,禁止拷贝构造防止重复释放
🛡️ 异常安全保证
- 所有可能失败的操作都抛出异常,确保程序的健壮性
- 构造函数不执行可能失败的操作,将初始化延迟到Open()方法
📋 核心成员变量分析
关键成员说明:
file_descriptor_:底层文件描述符,用于文件操作mapped_data_:内存映射区域的起始地址file_size_:映射文件的大小,用于边界检查mode_:映射模式,控制访问权限和共享特性
⚙️ 核心技术原理
🎪 内存映射机制
内存映射文件的核心原理是利用操作系统的虚拟内存管理机制,将文件内容直接映射到进程的地址空间。当进程访问映射区域时,操作系统自动处理页故障并将对应的文件数据加载到物理内存。
🔬 映射过程详解:
🛡️ 三种映射模式对比
| 模式 | 访问权限 | 共享特性 | 适用场景 |
|---|---|---|---|
| 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流程:
- 读取请求到内核
- 内核从磁盘读取数据到内核缓冲区
- 数据从内核缓冲区拷贝到用户空间
内存映射流程:
- 建立内存映射
- 直接访问虚拟内存地址
- 操作系统按需加载数据页
📊 性能对比
🔧 同步机制优化
类提供了灵活的同步策略,平衡性能和数据一致性:
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);
}
🎮 游戏开发
游戏资源文件通常使用内存映射进行快速加载:
- 纹理、模型等资源的快速加载
- 关卡数据的流式加载
- 存档文件的快速读写
🔍 测试框架分析
🧪 全面的测试覆盖
提供的测试代码实现了多层次的测试覆盖:
测试类型统计:
- ✅ 基本功能测试:映射创建、文件访问
- ✅ 模式测试:读写模式、写时复制
- ✅ 边界测试:越界访问、空指针处理
- ✅ 性能测试:大文件处理
- ✅ 异常测试:错误条件处理
📋 测试架构设计
🎯 测试亮点
- 隔离性:每个测试用例使用独立的测试文件
- 自动化:测试文件自动创建和清理
- 全面性:覆盖正常路径和异常路径
- 可重复性:使用随机文件名避免冲突
💡 实践建议
✅ 使用时机判断
适合使用内存映射的场景:
- 需要频繁随机访问大文件
- 文件大小相对稳定
- 需要与其他进程共享数据
- 性能要求极高的I/O操作
不适合使用的场景:
- 文件频繁扩展或收缩
- 需要精细的缓存控制
- 内存资源极度紧张
🔧 配置优化建议
- 页面大小对齐:确保访问按页面大小对齐以获得最佳性能
- 预读策略:对顺序访问模式启用预读
- 同步策略:根据数据重要性选择合适的同步频率
⚠️ 注意事项
- 资源限制:映射区域受虚拟地址空间限制
- 错误处理:mmap失败可能返回MAP_FAILED而非NULL
- 可移植性:不同系统对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;
}
更多推荐



所有评论(0)