
读取快照时最难检查的,往往是没有读成功的那条路径:open 成功后 fstat 失败,文件长度为零,mmap 返回 MAP_FAILED,或者映射后发现格式不兼容。把句柄放进几个未初始化字段,再指望析构函数统一收尾,可能会读到垃圾值。
C++ Core Guidelines 的 R.1 建议用资源句柄自动管理资源。原始指南 下面把文件描述符与映射分别交给对象管理,只演示 POSIX 主机上的只读映射,不涉及 CUDA。

#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <cstdio>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <memory>
#include <stdexcept>
struct Fd {
int value;
explicit Fd(int v) : value(v) {
if (v < 0) throw std::runtime_error("dup failed");
}
~Fd() { ::close(value); }
Fd(const Fd&) = delete;
Fd& operator=(const Fd&) = delete;
};
class ReadMap {
Fd fd_;
void* ptr_ = MAP_FAILED;
std::size_t size_ = 0;
public:
explicit ReadMap(int source_fd) : fd_(::dup(source_fd)) {
struct stat st{};
if (::fstat(fd_.value, &st) || st.st_size <= 0 || st.st_size > 1048576)
throw std::runtime_error("invalid size");
size_ = static_cast<std::size_t>(st.st_size);
ptr_ = ::mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd_.value, 0);
if (ptr_ == MAP_FAILED) throw std::runtime_error("mmap failed");
}
~ReadMap() { if (ptr_ != MAP_FAILED) ::munmap(ptr_, size_); }
ReadMap(const ReadMap&) = delete;
ReadMap& operator=(const ReadMap&) = delete;
const char* data() const { return static_cast<const char*>(ptr_); }
std::size_t size() const { return size_; }
};
std::uint32_t checksum(const char* p, std::size_t n) {
std::uint32_t h = 2166136261u;
for (std::size_t i=0; i<n; ++i)
h = (h ^ static_cast<unsigned char>(p[i])) * 16777619u;
return h;
}
void validate(const ReadMap& m, std::uint32_t expected) {
if (m.size() < 4 || std::memcmp(m.data(), "PFX1", 4) != 0)
throw std::runtime_error("bad header");
if (checksum(m.data(), m.size()) != expected)
throw std::runtime_error("bad checksum");
}
int main() {
using File = std::unique_ptr<std::FILE, int(*)(std::FILE*)>;
File file(std::tmpfile(), std::fclose);
if (!file) throw std::runtime_error("tmpfile failed");
const char bytes[] = "PFX1cached";
const std::size_t n = sizeof(bytes)-1;
if (std::fwrite(bytes, 1, n, file.get()) != n || std::fflush(file.get()))
throw std::runtime_error("write failed");
ReadMap map(::fileno(file.get()));
auto expected = checksum(bytes, n);
validate(map, expected);
bool rejected = false;
try { validate(map, expected ^ 1u); }
catch (const std::runtime_error&) { rejected = true; }
if (!rejected) throw std::runtime_error("bad checksum accepted");
std::cout << "map and checksum rejection passed\n";
}
在 POSIX 环境可用 clang++ -std=c++17 -Wall -Wextra -pedantic example.cpp -o example 编译,再运行 ./example。程序只使用临时文件;映射对象先析构,随后其 fd 成员析构,最后 FILE 对象收尾。复制被禁止,避免两个对象重复释放同一资源。
构造映射对象的过程中若 fstat 或 mmap 失败,已经构造完成的 fd 成员仍会释放。这里的关键是把每份资源放进自己的已建立对象,不能假定“最外层构造失败后,自己的析构函数仍会执行”。
FNV式校验值只演示与外部预期值比较,不是防伪认证。真实格式还需固定字段编码、长度、版本和张量身份,不能把本机结构体原样当跨平台协议。并发修改或截断映射文件也可能引发严重问题;读取方应使用发布完成、保持不变的快照。mmap 系统调用说明
该例覆盖小文件映射和错误校验拒绝,不是 GPU 恢复、存储带宽或崩溃一致性测试。析构中的系统调用返回值也没有构成完整的运维诊断方案;需要进一步记录失败、验证发布流程,并用真实快照做集成测试。
本文探讨C++中快照文件读取失败路径的健壮性设计,重点分析open、fstat、mmap等系统调用失败时的资源清理问题。强调遵循RAII原则,将文件描述符与内存映射分别封装为独立资源句柄,确保异常安全;指出零长度文件、MAP_FAILED、格式不兼容等边界情况的检测必要性,并说明FNV校验和在快照一致性验证中的辅助作用。

474

被折叠的 条评论
为什么被折叠?



