5

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

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

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

4

3 回答 3

7

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

POSIX 有

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

  • fsync(2) 保证等待数据发送到硬件,但需要一个文件描述符(您可以使用 fileno(3) 从 FILE* 获取一个,我不知道从一个IO 流)。

  • O_SYNC 作为 open(2) 的标志。

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

于 2012-11-13T09:44:11.653 回答
2

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

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

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

//... open and write data to a FILE *myfile
fflush(myfile);
fsync(fileno(myfile));
  • 注意 1:这些调用强制操作系统确保任何操作系统缓存中的任何数据都写入驱动器,并且驱动器承认这一事实。许多硬盘驱动器在这方面对操作系统撒谎,并且可能将数据填充到驱动器上的高速缓存中。
于 2012-11-13T10:00:51.800 回答
0

不在标准 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 );
}

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

于 2012-11-13T09:57:57.670 回答