• 正文
  • 相关推荐
申请入驻 产业图谱

嵌入式设备断电后文件系统损坏?

17小时前
287
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

大家好,我是杂烩君。

今天,我就以“日志乱码”为切入点,和大家聊聊嵌入式Linux文件系统损坏的那些事。这篇文章会尽量做到通俗易懂,同时也有实践验证,保证看完有收获。

1. 一个真实的场景

假设你的嵌入式设备正在运行,突然被拔掉电源(或者电池没电了)。重新上电后,你可能会发现:

# 原本正常的日志文件
[2026-06-15 10:30:01] System started
[2026-06-15 10:30:05] Network connected

# 变成了这样
[2026-06-15 10:30:01] System st�@�R�v�ected

这就是典型的“日志乱码”现象。为什么会这样?

2. 罪魁祸首:页缓存(Page Cache)

很多人认为,当我们调用write()函数写文件时,数据会立刻写入存储设备。这是一个常见的误区!

实际上,Linux为了提升性能,引入了页缓存(Page Cache)机制:

简单来说,你写入的数据先暂存在内存里,内核会在适当的时候(比如缓存满了、调用fsync()、系统空闲时)才真正写入存储设备。

这个设计在正常情况下非常高效,但在意外断电时却埋下了隐患——内存中的数据还没来得及写入存储设备就丢失了

3. 日志乱码的真正原因

断电不仅会导致最后几秒的日志丢失,更糟糕的是可能造成文件系统的元数据损坏,从而引发乱码。

举个具体的例子,日志文件在磁盘上是按“块”存储的。当追加日志时:

如果这些操作只完成了一半就断电了,就会出现一个逻辑上不一致的状态:文件系统认为文件有一个新块,但那个块里实际存储的是别的文件残留数据全零数据

重新读取时,这些“脏数据”被当作日志内容显示出来——乱码就这么产生了。

4. 动手实践:复现问题

说了这么多理论,我们来动手验证一下。

4.1 实验环境准备

# 使用一个U盘或SD卡作为测试设备
sudo dd if=/dev/zero of=/dev/sda1 bs=1M count=100
sudo mkfs.ext4 /dev/sda1
sudo mount /dev/sda1 /mnt/test

4.2 模拟意外断电

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main() {
    int fd = open("/mnt/test/log.txt", O_WRONLY | O_CREAT | O_APPEND, 0644);
    if (fd < 0) {
        perror("open");
        return1;
    }
    
    for (int i = 0; i < 10000000; i++) {
        char buf[256];
        sprintf(buf, "[%d] This is log entry number %dn", i, i);
        write(fd, buf, strlen(buf));
        
        // 注意:这里没有调用fsync()!
        // 数据只存在于页缓存中
    }
    
    close(fd);
    return0;
}

运行上面的程序后,突然拔掉电源。重新挂载文件系统,你很可能会看到乱码或文件系统错误。

4.3 对比实验:正确的做法

// 在write之后加上fsync
write(fd, buf, strlen(buf));
fsync(fd);  // 强制将数据同步到存储设备

加上fsync()后,每次写入都会确保数据落盘。虽然性能会下降,但数据安全性大大提升

5. 解决方案大盘点

针对嵌入式设备的特殊场景,这里整理了几种解决方案:

5.1 关键数据使用同步写入

// 打开文件时使用O_SYNC标志
int fd = open("/mnt/test/important.conf", O_WRONLY | O_CREAT | O_SYNC, 0644);

5.2 挂载时使用同步选项

mount -o sync /dev/sda1 /mnt/data

这会强制所有写入操作同步,适合存关键配置的分区。

5.3 使用日志文件系统

ext4、xfs等文件系统本身就支持日志(journal)功能。日志可以记录“将要做什么”,断电后通过重放日志恢复到一致状态:

# 创建ext4时启用日志
mkfs.ext4 -J size=32 /dev/sda1

5.4 考虑使用只读文件系统

对于不需要写入的分区(如系统分区),可以挂载为只读:

mount -o ro /dev/sda1 /system

6. 一些建议

根据不同场景,这里给出一些建议:

总结

在关键地方使用正确的同步策略,就能在性能和可靠性之间找到平衡。

对于嵌入式设备来说:

非关键数据:可以让内核自己决定何时回写

重要配置:必须确保同步写入

极致可靠:考虑硬件掉电保护和文件系统日志功能

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录

本公众号专注于嵌入式技术,包括但不限于C/C++、嵌入式、物联网、Linux等编程学习笔记,同时,公众号内包含大量的学习资源。欢迎关注,一同交流学习,共同进步!