【问题标题】:behavior of C++ ofstream in the event of program crash程序崩溃时 C++ ofstream 的行为
【发布时间】:2014-09-19 16:51:36
【问题描述】:

我正在尝试调试导致我的程序在执行过程中冻结的运行时错误。我创建了一个日志系统,它在执行过程中写入一个简单的日志文本文件。

我只是使用带有 std::string 类型消息的 ofstream 对象的流插入 (

void foo(){
// ... Code block 1 ...
// myLogger.Write (timestamp, "Code block1 successfully executed");
// ... Code block 2 ... PROGRAM FREEZE AND CRASH!
}

我的问题是:假设我的程序死机了,我需要从任务管理器中终止它的进程,我可以相信我的日志系统在崩溃点之前已经可靠地写入了所有消息吗?

或者,参考上面提到的代码,如果代码块 2 导致我的程序冻结,我可以相信“代码块 1 成功执行”消息确实会出现在我的日志文件中吗?

附:我在 Windows 上,我感到担心的原因是在奇怪的情况下我并不完全信任操作系统围绕文件权限的行为。我的程序确实具有文件的读/写权限,但我不知道崩溃期间会发生什么奇怪的事情。也许我只是偏执,如果是,请告诉我。

有没有更好的方法来检测崩溃发生在哪个代码块中?

【问题讨论】:

  • 您通常无法保证崩溃之前、之后或期间发生了什么。除非你有类似myLogger.flush() 的东西,它会显式地强制写入输出流。虽然这通常不需要用于记录系统目的。
  • @user3670482 使用try/catch() 块来捕获常规异常,为不规则异常安装信号处理程序(例如处理器 FPU 异常)。
  • @user3670482 好吧,flush() 可能会失败,如果你之前遇到了一些未定义的行为,或者内存不足的情况。
  • @user3670482 “为什么 flush() 不适合记录日志?” 因为它可以很好地改变程序流的行为,因为它引入了同步(阻塞写入),这使得很难检测多线程应用程序中的竞争条件,如果打开日志记录与关闭日志记录,竞争条件可能会消失。
  • @πάνταῥεῖ UB 导致时间旅行:blogs.msdn.com/b/oldnewthing/archive/2014/06/27/10537746.aspx

标签: c++ windows stream file-handling ofstream


【解决方案1】:

“我可以相信“代码块 1 已成功执行”消息确实会出现在我的日志文件中吗?”

不,当执行该语句时,您不能相信任何内容最终都会写入您的日志文件,并且您的程序实际上处于某种异常状态。

使用flush() 调用暗示写入行,机会会变得更好,但仍不能保证。这取决于您的程序遇到的异常情况。

考虑一种情况,您的程序由于未定义的行为而崩溃,这意外地弄乱了 Logger 类实例使用的缓冲区或数据中的某些内容。此外,如果您遇到了 内存不足 的情况,则无法保证后续执行的代码,即使它被正确捕获。


另请注意:
多线程应用程序上下文中的日志记录系统通常不需要同步刷新。 flush() 的同步调用可能会改变实际应用的线程的行为,并隐藏/混淆竞争条件,一旦关闭日志记录就会出现。

【讨论】:

    【解决方案2】:

    如果您想确保您的文本已写入日志文件,您需要使用 std::endl 执行刷新,将使用的任何缓冲区写入输出设备。

    例如:

    std::cout << "write me!\n";  // no flush
    
    std::cout << "write me!" << std::endl;  // buffer flushed 
    

    【讨论】:

    • “如果你想确定......”正如我在回答中提到的那样,冲洗并不能真正可靠。
    • @πάνταῥεῖ 如果您的进程没有崩溃,那么刷新输出流会将文本写出,不是吗?
    • 是的,如果 no crash 或异常程序状态 flush()endl 将强制写入文本。但这不是问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多