C++ 传递给std::ifstream的std::filebuf不总是被调用 目标

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

为旧式C++11程序禁用无缓冲I/O内核页面缓存。此功能必须按需提供(通过可执行参数)。其思想是减少I/O操作的内存开销,而不考虑性能。我不确定这是实现这一目标的正确方式,尽管

我的尝试 代码库相当大,大量使用
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::cout
std::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