【问题标题】:Force write of a file to disk强制将文件写入磁盘
【发布时间】:2012-11-01 17:53:26
【问题描述】:

我目前正在实施一个 ping/pong 缓冲方案,以安全地将文件写入磁盘。我在 Linux/CentOS 机器上使用 C++/Boost。现在我面临强制将文件实际写入磁盘的问题。无论文件系统(ext3/ext4)/SO 自定义规则/RAID 控制器/硬盘控制器的所有缓存策略如何,都可以这样做吗?

最好使用普通的 fread()/fwrite()、c++ ostream 还是 boost 文件系统?

我听说简单地刷新文件 (fflush()) 并不能保证实际写入

【问题讨论】:

    标签: c++ linux unix disk fflush


    【解决方案1】:

    fflush(用于 FILE*)、std::flush(用于 IOStream)强制您的程序发送到操作系统。

    POSIX 有

    • sync(2) 请求安排写入其缓冲区,但可以在写入完成之前返回(Linux 在返回之前等待数据发送到硬件)。

    • fsync(2) 保证等待数据发送到硬件,但需要一个文件描述符(您可以使用 fileno(3) 从 FILE* 获取一个,我知道没有标准一种从 IOStream 中获取的方法)。

    • O_SYNC 作为打开标志(2)。

    在所有情况下,硬件可能都有自己的缓冲区(但如果它可以控制它,一个好的实现也会尝试刷新它们,并且 ISTR 一些磁盘正在使用电容器,以便它们能够刷新发生的任何事情权力)和网络文件系统有自己的警告。

    【讨论】:

    • 对于 C++ 流,有操纵器 std::flush 而不是 fflush
    • 谢谢!所以我必须强制我的程序首先提交到文件系统(fflush/flush),然后强制 SO 提交到磁盘控制器(同步)。你能给我看一些概念验证代码吗?
    • @JoachimPileborg,这就是我想用过于简洁的 (f)flush 来表示的意思,我已经澄清了。
    • 我不得不指出,这些都不能保证数据被物理写入磁盘。它很可能只存在于硬盘缓存或计算机内部的其他神秘位置。
    • @rubenvb:这正是我所关心的。不幸的是,由于性能损失很大,禁用 RAID 控制器上的写入缓存不是一种选择。此外,解决方案应该只是软件。
    【解决方案2】:

    您可以使用fsync()/fdatasync() 将数据强制(注1)到存储中。 那些需要一个文件描述符,由例如给出。打开()。 linux manpage 有更多关于 linux 的具体信息,特别是关于 fsync 和 fdatasync 的区别。

    如果您不直接使用文件描述符,许多抽象将包含驻留在您的进程中的内部缓冲区。

    例如如果您使用 FILE*,您首先必须将数据从应用程序中清除。

    //... open and write data to a FILE *myfile
    fflush(myfile);
    fsync(fileno(myfile));
    
    • 注意 1:这些调用强制操作系统确保将任何操作系统缓存中的任何数据写入驱动器,并且驱动器承认这一事实。许多硬盘驱动器在这方面向操作系统撒谎,并可能将数据塞入驱动器的高速缓存中。

    【讨论】:

    • @G_G 完成 FILE* 后,关闭文件,然后使用 fclose()
    • 对不起,我的意思是直接使用文件描述符时;我们需要明确的 close(fd) 调用吗?
    • @G_G 如果您使用 FILE*,请使用 fclose。如果您使用文件描述符,请使用 close()。如果你从 FILE* 中提取文件描述符,你仍然有一个 FILE*,所以使用 fclose(它也会关闭文件描述符)
    【解决方案3】:

    不在标准 C++ 中。您必须使用某种特定于系统的 IO,比如open在Unix下带有O_SYNC标志,然后是write

    请注意,ostream(以及 C,FILE*) 被缓冲。如果你不知道某事的确切时间 写入磁盘,那么坚持 写入的事务完整性。 (不会太难 设计一个streambuf在您进行显式刷新时写入, 但是。)

    编辑:

    举个简单的例子:

    class SynchronizedStreambuf : public std::streambuf
    {
        int myFd;
        std::vector<char> myBuffer;
    
    protected:
        virtual int overflow( int ch );
        virtual int sync();
    
    public:
        SynchronizedStreambuf( std::string const& filename );
        ~SynchronizedStreambuf();
    };
    
    int SynchronizedStreambuf::overflow( int ch )
    {
        if ( myFd == -1 ) {
            return traits_type::eof();
        } else if ( ch == traits_type::eof() ) {
            return sync() == -1 ? traits_type::eof() : 0;
        } else {
            myBuffer.push_back( ch );
            size_t nextPos = myBuffer.size();
            myBuffer.resize( 1000 );
            setp( &myBuffer[0] + nextPos, &myBuffer[0] + myBuffer.size() );
            return ch;
        }
    }
    
    int SynchronizedStreambuf::sync()
    {
        size_t toWrite = pptr() - &myBuffer[0];
        int result = (toWrite == 0 || write( myFd, &myBuffer[0], toWrite ) == toWrite ? 0 : -1);
        if ( result == -1 ) {
            close( myFd );
            setp( NULL, NULL );
            myFd = -1;
        } else {
            setp( &myBuffer[0], &myBuffer[0] + myBuffer.size() );
        }
        return result;
    }
    
    SynchronizedStreambuf::SynchronizedStreambuf( std::string const& filename )
        : myFd( open( filename.c_str(), O_WRONLY | O_CREAT | O_SYNC, 0664 ) )
    {
    }
    
    SynchronizedStreambuf::~SynchronizedStreambuf()
    {
        sync();
        close( myFd );
    }
    

    (这只是表面上的测试,但基本的想法是有的。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-01
      相关资源
      最近更新 更多