2012-11-13 126 views
1

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

最好是使用普通的fread()/ fwrite(),C++ ostream或boost文件系统吗?

我听说,只是冲洗出来的文件(fflush())并不能保证实际写入

谢谢!

回答

5

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

POSIX有

  • 同步(2)要求安排写其缓冲区,但可以写入完成之前返回(Linux正在等待数据是在返回前发送到硬件)。 (2)这是保证等待数据发送到硬件,但需要一个文件描述符(你可以从FILE *获得一个fileno(3),我知道没有标准的方式从IOStream获得一个)。

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

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

+0

而对于C++流,操作符是'std :: flush'而不是'fflush'。 –

+0

谢谢!所以我必须强制我的程序首先提交文件系统(fflush/flush),然后强制SO将其提交到磁盘控制器(sync)。你能告诉我一些概念验证码吗? –

+0

@JoachimPileborg,这就是我打算用太简洁(f)冲洗来表示的,我澄清了。 – AProgrammer

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); 
} 

(这只是表面测试过的,但基本思路就是这样。)

1

您可以使用fsync()/ fdatasync() ce(注1)将数据存入存储器。 这些请求文件描述符,例如,打开()。 linux manpage有更多的linux特定的信息,特别是在fsync和fdatasync的区别。

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

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

//... open and write data to a FILE *myfile 
fflush(myfile); 
fsync(fileno(myfile)); 
  • 注1:这些调用强制操作系统,以确保在任何操作系统的缓存上的所有数据被写入到驱动器,并且驱动承认这一事实。许多硬盘驱动器都在操作系统中,可能会将数据填充到驱动器上的高速缓存中。
+0

fclose()呢?我们需要它吗? –

+0

@G_G当你完成文件*,关闭文件,然后使用fclose() – nos

+0

对不起,我的意思是当直接使用文件描述符;我们是否需要明确的关闭(fd)呼叫? –