iT邦幫忙

2026 iThome 鐵人賽

DAY 27
0
Software Development

從零打造邊緣運算閘道器:Raspberry Pi 與 Linux 底層軟硬整合實戰系列 第 27

Day 27:斷電與拔插救援:如何實作開機自動化 fsck 檢查與壞軌修復?

  • 分享至 

  • xImage
  •  

🎯 問題情境 (The Problem)

邊緣運算閘道器 (EdgeNode) 通常部署於現場環境(如醫院病房、工業車間、遠端監測站),這些場景存在兩個無法避免的極端狀況:

  1. 非預期突發斷電 (Unclean Shutdown / Hard Power Cut):台電跳電、現場人員不小心拔掉電源線。
  2. 實體熱拔插 (Hot Unplugging):使用者在 EdgeNode 正在寫入資料時,直接強行拔除 USB 隨身碟。

在 Linux 檔案系統中,當寫入操作進行到一半突然中斷,磁碟的中繼資料 (Metadata) 尚未同步 (fsync),這會導致:

  • Dirty Bit (髒旗標) 被標記:FAT32 / ext4 標頭標記為「未正常卸載」。
  • 檔案系統遺失或掛載成唯讀 (Read-Only File System):Linux 核心為了保護磁碟,自動將掛載點鎖定為唯讀,導致後續所有續寫操作一律跳出 IOError: [Errno 30] Read-only file system
  • 開機卡死在 Emergency Mode:樹莓派重新開機時,systemd-fsck 發現 Root Partition 或 Loopback Image 損壞且無法自動修正,導致系統卡在緊急復原命令列,設備徹底「磚化」。

在 SRE 穩定度工程中,「無人值守的自癒能力 (Self-Healing File System)」是硬體產品能否存活的生死關鍵


❌ 錯誤嘗試 (The Pitfalls)

在面對磁碟損壞救援時,開發者經常採用以下無效或危險的作法:

1. 在 /etc/fstab 中停用檢驗 (passnum = 0)

有些工程師為了避免開機卡住,直接把 /etc/fstab 最後一欄改為 0

❌ 危險作法:完全放棄檢查

/opt/gateway/data/storage.img /mnt/datadock vfat defaults,nofail 0 0

這只不過是掩耳盜盜。損壞的檔案系統依然存在,資料繼續寫入只會讓亂碼與檔案節點 (Orphaned Inodes) 互相覆蓋,最終導致整個磁碟分區徹底崩潰。

2. 盲目執行 fsck -y

在修復邏輯中直接下 fsck -y 雖然會自動回答 yes,但在 FAT32 檔案系統中,若未指定適當參數,fsck 可能會直接將有問題的檔案區段抹除 (Truncate),造成關鍵數據徹底遺失。

3. 忽略 Loopback 虛擬磁碟的修復

多數人只注意到 SD 卡實體分區 (/dev/mmcblk0p2) 的救援,卻忽略了透過 dd 建立的 Loopback 虛擬隨身碟映像檔 (storage.img) 同樣會因為突發斷電而損壞。


🧠 底層原理:FAT32 Dirty Bit 與 dosfsck 修復機制

1. FAT32 的 Dirty Bit 標籤

FAT32 檔案系統在標頭區段 (Boot Sector) 留有狀態標誌。當 Linux 掛載 (Mount) 該分區時,會在 Boot Sector 寫入 Dirty 旗標;只有當執行正常 umount 時,才會將該旗標清除。

https://ithelp.ithome.com.tw/upload/images/20260912/20183645aRO4D5krEB.png

2. fsck.vfat (dosfsck) 修復策略

Linux 針對 VFAT/FAT32 提供了專用的修復工具 fsck.vfat (即 dosfsck)。關鍵參數意義如下:

  • -a (Auto):自動修復檔案系統,不提出任何互動式詢問(適合 Headless 無頭設備)。
  • -w (Write immediately):修復完成後立即將變更寫回磁碟。
  • -r (Interactive):遇重大損壞時嘗試互動修復(不適用於無螢幕自動化開機)。

💡 最終解決方案:開機自動化修復腳本與 systemd 整合

為了確保 EdgeNode 在每次開機或載入 USB Gadget 前,檔案系統皆處於 100% 健康狀態,我們實作了修復腳本與守護進程卡位機制。

1. 自動化修復與掛載防護腳本 (fix_storage.sh)

此腳本負責檢測實體 SD 卡與 Loopback Image,並在掛載前自動執行修復:

#!/bin/bash
# /opt/gateway/scripts/fix_storage.sh
# EdgeNode 磁碟自癒與修復機制

STORAGE_IMG="/opt/gateway/data/storage.img"
MOUNT_POINT="/mnt/datadock"

echo "[Storage Repair] 啟動磁碟自我修復檢查..."

# 1. 檢查 Loopback 檔案是否存在,若缺失則自動重建預設 128MB 虛擬磁碟
if [ ! -f "$STORAGE_IMG" ]; then
    echo "[Storage Repair] ⚠️ 警告:找不到磁碟映像檔,重新建立..."
    mkdir -p /opt/gateway/data
    dd if=/dev/zero of="$STORAGE_IMG" bs=1M count=128 status=none
    mkfs.vfat -F 32 -n "EDGENODE" "$STORAGE_IMG"
    echo "[Storage Repair] ✅ 虛擬磁碟重建完成!"
fi

# 2. 執行 dosfsck 自動修復 Loopback Image (FAT32)
echo "[Storage Repair] 正在掃描並修復 FAT32 檔案系統 ($STORAGE_IMG)..."
fsck.vfat -a -w "$STORAGE_IMG"
FSCK_RET=$?

if [ $FSCK_RET -eq 0 ]; then
    echo "[Storage Repair] ✅ 檔案系統健康,無須修復。"
elif [ $FSCK_RET -eq 1 ]; then
    echo "[Storage Repair] 🔧 檔案系統存在些許錯誤,已成功自動修正!"
else
    echo "[Storage Repair] ❌ 檔案系統嚴重損壞 (Code: $FSCK_RET),強制執行深度復原..."
    fsck.vfat -y -w "$STORAGE_IMG"
fi

# 3. 安全掛載測試
mkdir -p "$MOUNT_POINT"
if ! mountpoint -q "$MOUNT_POINT"; then
    mount -o loop,rw,utf8,uid=1000,gid=1000 "$STORAGE_IMG" "$MOUNT_POINT"
    echo "[Storage Repair] ✅ 成功將健康磁碟掛載至 $MOUNT_POINT"
fi

exit 0

2. 整合至 systemd 開機前置任務 (ExecStartPre)

在 EdgeNode 的主服務 datagateway.service 中,利用 ExecStartPre 確保在主程式啟動前完成修復:

# /etc/systemd/system/datagateway.service
[Unit]
Description=EdgeNode Gateway Main Service
After=network.target local-fs.target
Wants=local-fs.target

[Service]
Type=simple
User=root
WorkingDirectory=/opt/gateway

# 開機前置防線:先強制執行修復腳本
ExecStartPre=/usr/bin/bash /opt/gateway/scripts/fix_storage.sh

# 啟動主控制程式
ExecStart=/opt/gateway/venv/bin/python3 src/main.py

Restart=always
RestartSec=3s

[Install]
WantedBy=multi-user.target

🎯 小結

在 Day 27 中,我們解決了現場設備斷電導致「磚化」的惡夢:

  1. 理解 FAT32 Dirty Bit 物理特性:明白非正常關機為何會觸發唯讀鎖定。
  2. 自動化 fsck.vfat 救援:透過 -a -w 參數實現 Headless 設備無人值守修復。
  3. systemd 前置防線:利用 ExecStartPre 確保每次啟動前磁碟映像檔均為 100% 可讀寫狀態。

在下一篇 Day 28 中,我們將探討另一個系統級的陷阱:「系統服務化:systemd 守護神與 journalctl 中文亂碼 (Locale) 的坑」


上一篇
Day 26:開源前的準備:實戰機敏資訊去敏與自動化發布檢查
系列文
從零打造邊緣運算閘道器:Raspberry Pi 與 Linux 底層軟硬整合實戰27
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言