如何使用C++ ifstream避免双缓冲读取/proc/self/smaps
问题:C++ ifstream read 是否存在双缓冲?
我原本用C语言读取/proc/self/smaps到缓冲区的代码(已简化,忽略错误处理,假设缓冲区足够):
char* buff = (char*)malloc(2 * 1024 * 1024); // 2MB 缓冲区 int fd = open("/proc/self/smaps", O_RDONLY); char* buff_it = buff; char* buff_end = buff_it + buff_sz - 1; ssize_t read_sz; while ((read_sz = read(fd, buff_it, buff_end - buff_it)) > 0) buff_it += read_sz; close(fd); // 解析 buff free(buff);
现在改用C++的ifstream实现:
auto buff = std::make_unique<char[]>(2 * 1024 * 1024); std::ifstream ifs("/proc/self/smaps", std::ios::binary); ifs.read(buff.get(), 2 * 1024 * 1024); buff[ifs.gcount()] = '\0';
我的疑问是:ifstream的read函数内部会不会用一个私有缓冲区先存输入,再复制到我提供的缓冲区里?也就是这段代码是不是存在双缓冲?这会浪费资源。我希望ifstream直接用我提供的2MB缓冲区填充数据,关闭文件后再自行解析。
回答
默认情况下,std::ifstream确实会使用内部的缓冲区(由其关联的std::streambuf管理),这意味着你的代码会存在双缓冲:内核先把数据读到ifstream的内部缓冲区,之后read()再把数据从内部缓冲区复制到你提供的buff里,额外消耗了内存和复制开销。
要让ifstream直接使用你提供的缓冲区,有两种可行方案:
方案1:替换ifstream的内部缓冲区
你可以调用std::filebuf的pubsetbuf方法,指定自己的缓冲区作为ifstream的内部缓冲,示例代码如下:
auto buff = std::make_unique<char[]>(2 * 1024 * 1024); const size_t buff_size = 2 * 1024 * 1024; std::ifstream ifs; ifs.rdbuf()->pubsetbuf(buff.get(), buff_size); // 设置自定义缓冲区 ifs.open("/proc/self/smaps", std::ios::binary); // 读取全部内容到自定义缓冲区 ifs.read(buff.get(), buff_size); buff[ifs.gcount()] = '\0';
注:pubsetbuf的行为在不同标准库实现中可能有细节差异,但主流实现(如GCC的libstdc++、Clang的libc++)都支持通过该方法替换内部缓冲区,完全避免双缓冲。
方案2:直接使用std::filebuf操作
如果不想依赖ifstream的上层封装,可以直接操作std::filebuf,更直接地控制缓冲区:
auto buff = std::make_unique<char[]>(2 * 1024 * 1024); const size_t buff_size = 2 * 1024 * 1024; std::filebuf fb; if (fb.open("/proc/self/smaps", std::ios::in | std::ios::binary)) { fb.pubsetbuf(buff.get(), buff_size); // 读取数据直接写入自定义缓冲区 std::streamsize bytes_read = fb.sgetn(buff.get(), buff_size); buff[bytes_read] = '\0'; fb.close(); }
这种方式完全跳过ifstream的额外封装,确保没有多余的内部缓冲开销。
需要注意的细节:
- 自定义缓冲区的生命周期必须长于
std::ifstream或std::filebuf的生命周期,否则会出现悬空指针问题。 - 对于
/proc这类虚拟文件,其大小可能在读取过程中变化,但你已假设缓冲区足够,因此无需额外处理。
内容的提问来源于stack exchange,提问作者ABu
相关产品推荐
相关产品推荐

