C++ 传递给std::ifstream的std::filebuf不总是被调用 目标
为旧式C++11程序禁用无缓冲I/O和内核页面缓存。此功能必须按需提供(通过可执行参数)。其思想是减少I/O操作的内存开销,而不考虑性能。我不确定这是实现这一目标的正确方式,尽管 我的尝试 代码库相当大,大量使用C++ 传递给std::ifstream的std::filebuf不总是被调用 目标,c++,linux,fstream,file-pointer,filebuf,C++,Linux,Fstream,File Pointer,Filebuf,为旧式C++11程序禁用无缓冲I/O和内核页面缓存。此功能必须按需提供(通过可执行参数)。其思想是减少I/O操作的内存开销,而不考虑性能。我不确定这是实现这一目标的正确方式,尽管 我的尝试 代码库相当大,大量使用std::ifstream和std::ofstream在不同的二进制文件/库之间传播,我的目标是实现一个派生自std::filebuf的类,该类依赖于C I/O特性(FILE*,open()这样我就可以传递O_DIRECT标志等),并使用继承的方法std::basic_streambuf
std::ifstream
和std::ofstream
在不同的二进制文件/库之间传播,我的目标是实现一个派生自std::filebuf
的类,该类依赖于C I/O特性(FILE*
,open()
这样我就可以传递O_DIRECT
标志等),并使用继承的方法std::basic_streambuf*std::basic_ios::rdbuf(std::basic_streambuf*)将其传递给ifstream
对象(目前仅输入)
问题
问题在于std::ifstream
对象实际上似乎有两个内部缓冲区。请参阅代码以了解我的实验(可能仍然存在一些明显的错误)
我的文件buf
//FileBuf.h
类FileBuf:public std::FileBuf{
公众:
FileBuf();
虚拟~FileBuf();
虚拟std::filebuf*打开(常量字符*文件名,std::ios\u base::openmode);
虚拟std::filebuf*open(常量std::字符串文件名,std::ios\u base::openmode模式);
虚拟布尔是_open()常量;
虚拟std::filebuf*close();
虚拟std::streambuf*setbuf(字符类型*s,std::streamsize n);
虚拟int_类型溢出(int c=traits_类型::eof());
虚拟文件buf::int_type underflow();
虚拟整数同步();
私人:
国际货币基金组织;
文件*\u fp;
char _buff[1];//最小大小
};
//FileBuf.cpp
FileBuf::FileBuf()
:std::filebuf(),_fd(0),_fp(NULL)
{}
FileBuf::~FileBuf(){
close();//RAII
}
std::filebuf*filebuf::open(const char*filename,std::ios\u base::openmode){
std::coutstd::ifstream
始终将与它自己的std::filebuf
一起使用。std::filebuf
与std::basic_ios
所拥有的是分开的。设置是无效的,因为它从未使用过,并且std::ifstream
始终使用它自己的。另请参阅
相反,您可以使用自己的实现覆盖libstdc++中文件输入/输出操作的实现。从中获取原始实现,并使用自定义行为对其进行修补。动态链接器将首选您的符号而不是共享符号。例如:
#include <iostream>
#include <sstream>
#include <fstream>
#include <sys/fcntl.h>
#include <bits/basic_file.h>
#include <fcntl.h>
#include <errno.h>
#include <cstring>
#include <unistd.h>
// code copied from libstdc++-v3/config/io/basic_file_stdio.cc
namespace {
// Map ios_base::openmode flags to a string for use in fopen().
// Table of valid combinations as given in [lib.filebuf.members]/2.
static const char*
fopen_mode(std::ios_base::openmode mode)
{
enum
{
in = std::ios_base::in,
out = std::ios_base::out,
trunc = std::ios_base::trunc,
app = std::ios_base::app,
binary = std::ios_base::binary
};
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 596. 27.8.1.3 Table 112 omits "a+" and "a+b" modes.
switch (mode & (in|out|trunc|app|binary))
{
case ( out ): return "w";
case ( out |app ): return "a";
case ( app ): return "a";
case ( out|trunc ): return "w";
case (in ): return "r";
case (in|out ): return "r+";
case (in|out|trunc ): return "w+";
case (in|out |app ): return "a+";
case (in |app ): return "a+";
case ( out |binary): return "wb";
case ( out |app|binary): return "ab";
case ( app|binary): return "ab";
case ( out|trunc |binary): return "wb";
case (in |binary): return "rb";
case (in|out |binary): return "r+b";
case (in|out|trunc |binary): return "w+b";
case (in|out |app|binary): return "a+b";
case (in |app|binary): return "a+b";
default: return 0; // invalid
}
}
}
namespace std
{
__basic_file<char>*
__basic_file<char>::open(const char* __name, ios_base::openmode __mode,
int /*__prot*/)
{
__basic_file* __ret = NULL;
const char* __c_mode = fopen_mode(__mode);
if (__c_mode && !this->is_open())
{
// HERE I ADDED THIS LINE HERE I ADDED THIS LINE HERE I ADDED THIS LINE HERE I ADDED THIS LINE
const char *str = "TODO: set O_DIRECT here\n";
write(STDOUT_FILENO, str, strlen(str));
#ifdef _GLIBCXX_USE_LFS
if ((_M_cfile = fopen64(__name, __c_mode)))
#else
if ((_M_cfile = fopen(__name, __c_mode)))
#endif
{
_M_cfile_created = true;
__ret = this;
}
}
return __ret;
}
}
int main() {
std::string buff(1024, '\0');
std::ifstream ifs;
ifs.open("/tmp/1.cpp");
ifs.read(&buff[0], 1024);
}
您必须将fopen
替换为open+fdopen
,还必须替换\u basic\u file::close()
因此它也会close(fileno(..)
出于好奇,这一目标背后的动机是什么?您能从这三个代码片段中提取一个大代码片段,添加int main()
到第三个代码段,并添加缺少的#包含s?当代码易于复制、运行和测试时,这会很有帮助…|不惜一切代价-用自定义实现替换所有std::ifstream引用
,但您必须做些什么。是否在每个std::ifstream
中添加另一个FileBuf
然后再向每个构造函数添加另一个…rdbuf(&filebuf)
调用,然后替换声明?感谢您的帮助。此更改意味着我的程序的所有打开/关闭操作都将执行此操作?如何使其依赖于可执行参数?此外,我认为我不需要提供自己的关闭()实现,因为它已经执行了一个fclose(\u M\u cfile)
,这将自动使通过open()获得的关联文件描述符无效
此更改意味着我的程序的所有打开/关闭操作都将执行此操作?
是。如何使其依赖于可执行参数?
它是打开的(const char*\uu name
。比较\uu name
,这将自动使使用open()获得的关联文件描述符无效)
Och,你说得对,我一直认为fdopen
不能关闭它。
#include <iostream>
#include <sstream>
#include <fstream>
#include <sys/fcntl.h>
#include <bits/basic_file.h>
#include <fcntl.h>
#include <errno.h>
#include <cstring>
#include <unistd.h>
// code copied from libstdc++-v3/config/io/basic_file_stdio.cc
namespace {
// Map ios_base::openmode flags to a string for use in fopen().
// Table of valid combinations as given in [lib.filebuf.members]/2.
static const char*
fopen_mode(std::ios_base::openmode mode)
{
enum
{
in = std::ios_base::in,
out = std::ios_base::out,
trunc = std::ios_base::trunc,
app = std::ios_base::app,
binary = std::ios_base::binary
};
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 596. 27.8.1.3 Table 112 omits "a+" and "a+b" modes.
switch (mode & (in|out|trunc|app|binary))
{
case ( out ): return "w";
case ( out |app ): return "a";
case ( app ): return "a";
case ( out|trunc ): return "w";
case (in ): return "r";
case (in|out ): return "r+";
case (in|out|trunc ): return "w+";
case (in|out |app ): return "a+";
case (in |app ): return "a+";
case ( out |binary): return "wb";
case ( out |app|binary): return "ab";
case ( app|binary): return "ab";
case ( out|trunc |binary): return "wb";
case (in |binary): return "rb";
case (in|out |binary): return "r+b";
case (in|out|trunc |binary): return "w+b";
case (in|out |app|binary): return "a+b";
case (in |app|binary): return "a+b";
default: return 0; // invalid
}
}
}
namespace std
{
__basic_file<char>*
__basic_file<char>::open(const char* __name, ios_base::openmode __mode,
int /*__prot*/)
{
__basic_file* __ret = NULL;
const char* __c_mode = fopen_mode(__mode);
if (__c_mode && !this->is_open())
{
// HERE I ADDED THIS LINE HERE I ADDED THIS LINE HERE I ADDED THIS LINE HERE I ADDED THIS LINE
const char *str = "TODO: set O_DIRECT here\n";
write(STDOUT_FILENO, str, strlen(str));
#ifdef _GLIBCXX_USE_LFS
if ((_M_cfile = fopen64(__name, __c_mode)))
#else
if ((_M_cfile = fopen(__name, __c_mode)))
#endif
{
_M_cfile_created = true;
__ret = this;
}
}
return __ret;
}
}
int main() {
std::string buff(1024, '\0');
std::ifstream ifs;
ifs.open("/tmp/1.cpp");
ifs.read(&buff[0], 1024);
}
TODO: set O_DIRECT here