我目前正在实施一个 ping/pong 缓冲方案来安全地将文件写入磁盘。我在 Linux/CentOS 机器上使用 C++/Boost。现在我面临强制将文件实际写入磁盘的问题。无论文件系统(ext3 / ext4)/ SO自定义规则/ RAID控制器/硬盘控制器的所有缓存策略如何,都可以这样做吗?
最好使用普通的 fread()/fwrite()、c++ ostream 还是 boost 文件系统?
我听说简单地刷新文件 (fflush()) 并不能保证实际写入
fflush(用于 FILE*)、std::flush(用于 IOStream)强制您的程序发送到操作系统。
POSIX 有
sync(2) 要求安排写入其缓冲区,但可以在写入完成之前返回(Linux 正在等待数据在返回之前发送到硬件)。
fsync(2) 保证等待数据发送到硬件,但需要一个文件描述符(您可以使用 fileno(3) 从 FILE* 获取一个,我不知道从一个IO 流)。
O_SYNC 作为 open(2) 的标志。
在所有情况下,硬件可能都有自己的缓冲区(但如果它可以控制它,一个好的实现也会尝试刷新它们,并且 ISTR 某些磁盘正在使用电容器,以便它们能够刷新电源发生的任何事情)和网络文件系统有自己的警告。
您可以使用fsync ()/ fdatasync () 将数据强制(注 1)到存储上。那些需要一个文件描述符,由例如 open() 给出。linux 手册页有更多的linux 特定信息,特别是关于 fsync 和 fdatasync 的区别。
如果您不直接使用文件描述符,许多抽象将包含驻留在您的进程中的内部缓冲区。
例如,如果您使用 FILE*,您首先必须将数据从应用程序中清除。
//... open and write data to a FILE *myfile
fflush(myfile);
fsync(fileno(myfile));
不在标准 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 );
}
(这只是表面上的测试,但基本的想法是存在的。)