C++ - 即使在突然断电的情况下如何将应用程序日志写入磁盘

C++ - How to make application-logs written to disk even in case of a sudden power down

小背景:

我正在无人机上开发嵌入式 Linux 应用程序。 在我们进行的最后一次现场测试中,电池电量 运行 在飞行过程中没有任何警告。无人机艰难着陆。 当我在实验室重新启动它时,在 20 个日志文件(不同应用程序模块的 *CSV 和 *TXT)中,只有 3 个有数据,其余的完全是空的,但显然,它们有!拥有数据和大量数据。 (14 分钟飞行 = 840 秒,在 50Hz 频率下,我们的日志中预计有万分之一的数据行)。

所以我有两个问题:

  1. 是否有可能虽然某些日志每隔几秒(或更少 - 可能是每个写入命令)刷新一次,但其他日志保存在一个大缓冲区中,仅在干净断电或缓冲区何时耗尽?

  2. 鉴于我们使用的是 C++,最好的做法是使所有日志更频繁地刷新到磁盘(所以在突然断电的情况下,我还有一些日志要检查),并且对性能的影响仍然尽可能小?

谢谢

这里的根本问题是您的程序、您的语言、您的操作系统和您的硬件可能都在缓冲写入,以便将它们延迟到最合适的时间。

您真的在刷新每次写入时遇到性能问题吗?或者这只是理论上的?

根据您的情况使用以下一项或多项建议。

  1. 优化您的日志记录。你真的需要记录那么多吗?删除只有在进行开发时才应该存在的调试内容,而不是在进行测试或生产工作时。等待处理器不忙时刷新排队的日志。使用单独的线程进行日志记录(线程增加了太多的麻烦,除非没有其他解决问题的方法,否则不值得付出努力)。也许改为写入系统日志? (我不知道 syslog 是否具有很棒的日志刷新功能)。由于无人机与基站或控制器保持联系,您是否可以仅通过无线连接登录,甚至不在无人机中进行日志的实际磁盘存储?
  2. 如果您确实尝试过刷新每次写入(或时不时地)并且它会大大降低您的应用程序速度,请确保您的 Linux 上没有其他垃圾 运行窃取 CPU 或添加磁盘 read/writes 而你甚至没有使用的环境。
  3. 总是用一个函数调用写一个完整的记录(或多个连接的记录)。不要用单独的函数编写单独的记录部分。如果某件事失败了,你最终会得到一份部分书面记录。首先将记录中的所有元素组合在内存中,然后写下整件事,砰,完成。 Linux 写入似乎是原子的。一旦开始,它们将完全完成,没有其他人会打扰它们,但请确保您以追加模式打开文件,以避免混淆写入的位置。使用追加模式,写入将始终在文件末尾,即使其他人在您之前写入。多个作者听起来不像是你必须处理的问题。
  4. 不要为了速度而在你的申请中缓冲(你可能有其他原因)。那是老式的废话,可能是由一位知识过时的教授教给你的。您的操作系统可能已经有一个写缓冲区。你不需要自己做,事实上,如果多个进程需要共享文件,那会导致很多问题。
  5. 不要使用 1970 年代已有 50 年历史的 C 函数,如 fwrite 或 iostreams >> 令人厌恶的东西(它们只是概念证明,以某种方式被改编为标准)。他们可能会做自己的内部缓冲。使用操作系统的 write() 函数。如果您的 OS 是 Linux,请执行“man 2 write”。当然,您还必须使用其他 OS 级别的功能来打开和关闭文件。做“man 2 open”。有各种各样的选项来控制磁盘缓存等。您可以打开一个属性为 O_DIRECT 的文件,意思是“要求操作系统最小化缓存”和 O_DSYNC 这意味着您的写入不会 return 直到它被完全刷新到磁盘。这将尽可能绕过 OS 级别的写入缓冲区。这取决于您和您的要求。
  6. 有时可以使用“异步”功能(执行“man aio”),您不必等待写入完成。操作系统将为您管理这些,并至少保持最佳状态。就算落后了,只要一次写满记录,万一出问题了,也能把乱码降到最低。
  7. 您想使用像 Linux ext4 这样的“日志式”文件系统,在确认它们已完全写入磁盘之前,它实际上不会将写入追加到文件中。如果出现问题,当重新安装磁盘时,它会自行修复(尽其所能)。

我必须编写一个具有文件完整性的应用程序,并且我确实需要缓冲写入(因为处理期间的事件可能会告诉应用程序“不要写入那些排队的写入,我们改变主意”)。为了避免复杂化,所有内容都组合成一个字符串,然后在时机成熟时用一个 write() 函数写入。我从未有过损坏的文件,即使应用程序关闭也会杀死 -9。但是,它从未发生过电源故障。 (UPS 备份)。

如果您使用的是一个非常低效的平台,那么上面的很多内容可能都没有用。抱歉,您必须处理的内容不够详细。