本篇介紹如何在 Linux 中縮減磁碟分割區大小,剪裁磁碟影像檔,讓影像檔可以寫入較小的 MicroSD 記憶卡中。

樹莓派可以使用 dd 指令備份與回復影像檔,如果備份與回復所使用的 MicroSD 卡都是同一張卡的話,使用簡單的 dd 指令就可以處理所有的工作了,但是如果我們想要從一張 MicroSD 卡備份影像檔,然後將影像檔寫入另外一張卡,也就是複製一張一模一樣的 MicroSD 卡,在這樣的狀況下回復影像檔時就有可能會出現 MicroSD 卡空間不足的問題,造成 dd 在寫入時出現錯誤。


這個問題發生的原因是由於不同廠牌、型號的 MicroSD 卡,即使規格相同(例如都是 16 GB 的 MicroSD),實際上的除從空間大小都會有一點點的差異,有個卡稍微大一點、有的稍微小一點,如果剛好從比較小的卡備份影像檔,回復至比較大的卡,就不會有問題,但若是反過來的話,就會因為差一點點的空間大小,導致影像檔尾端的資料寫不進小的 MicroSD 卡,造成錯誤。

解決的方法是把備份出來的影像檔修改一下,把分割區稍微調小一些,然後裁剪影像檔,讓 dd 指令可以把影像檔順利寫入,以下是在 Linux 系統上縮小影像檔的操作步驟。

建立 Loopback 設備

若想要修改磁碟分割表,就必須先讓 Linux 核心載入磁碟,才能使用各種磁碟分割工具來修改,但是現在我們的磁碟不是一般的實體磁碟,而是一個影像檔,若要讓 Linux 核心可以載入這個磁碟,就必須使用 loopback 設備來處理。

首先啟用 Linux 核心 的 loopback 模組:

sudo modprobe loop

接著找尋尚未被使用的 loopback 設備:

sudo losetup -f
/dev/loop0

這個輸出表示 /dev/loop0 這個設備尚未被使用,所以我們可以使用這個名稱來建立一個影像檔的 loopback 設備:

sudo losetup /dev/loop0 my_image.img

其中 my_image.img 就是從 MicroSD 卡備份下來的影像檔,現在 /dev/loop0 就代表了 my_image.img 影像檔中的磁碟,接著要讓 Linux 核心也載入這個磁碟:

sudo partprobe /dev/loop0

這時候在系統上應該就可以看得到這個磁碟了(但是應該是處於未掛載的狀態)。

調整分割區

在使用 loopback 載入影像檔中的的磁碟之後,就可以使用普通的磁碟工具來修改它了。

一開始要調整分割區的大小,讓磁碟末端騰出一些空間,這個動作可以使用 GParted 的操作介面來處理:

sudo gparted /dev/loop0

原本的磁碟分割狀態會類似這樣,整個磁碟幾乎都用滿了,但某些分割區裡面還有些空間:

磁碟分割表

使用 GParted 的介面調整一下分割區的大小或位置,讓磁碟末端騰出一些空間,通常這個空間也不需要很大,就看影像檔的大小與 MicroSD 卡的容量相差多少,就騰出多少即可,如果磁碟分割區內的資料很少,多騰出一點空間也沒有關係:

磁碟分割表

使用 GParted 調整磁碟分割區時,結果會直接寫入我們的影像檔,修改好影像檔之後,即可卸載 loopback 設備:

sudo losetup -d /dev/loop0

裁剪影像檔

現在我們的影像檔的狀態應該就會像 GParted 介面中顯示的那樣,末端是沒有使用的空間,最後只要將那段沒用的空間裁剪掉即可。

首先以 fdisk 檢查影像檔內部使用的狀況:

fdisk -l my_image.img
Disk my_image.img: 14.9 GiB, 15987638272 bytes, 31225856 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x03ca9076

Device        Boot Start      End  Sectors  Size Id Type
my_image.img1       8192    96453    88262 43.1M  c W95 FAT32 (LBA)
my_image.img2      98304 27054079 26955776 12.9G 83 Linux

從這個輸出中可以看出每個分割區在磁碟中的開始位置(Start)與結束位置(End),最後一個分割區結束的位置是 26955776,也就是說在這個位置之後的空間,就是沒有使用到的部份,也就是可以裁剪掉的部份。

這裡的輸出中也有說明一個 sector 的大小是 512 位元組(bytes),而 sector 的計算方式是從 0 開始起算的,所以整個磁碟從開頭算起,一直到有資料的最末端,總共的大小應該是 (26955776+1)*512 位元組(bytes)。

計算出資料在磁碟中最末端的位置之後,就可以使用 truncate 裁剪影像檔,只留下前面有資料的部份:

# 裁剪影像檔
truncate --size=$[(27054079+1)*512] my_image.img

裁剪影像檔必須很謹慎,萬一弄錯資料就毀了,若是不確定的話,記得先備份影像檔,以免出錯造成資料損毀救不回來。

這樣裁剪完之後的影像檔,通常都或比任何同規格的 MicroSD 卡容量要小很多,所以使用 dd指令寫入的話一定沒問題,不過寫入完成後,末端也會出現一些未使用的空間(因為當初騰出了不少空間),這部份就再用 GParted 調整一下即可,或是在樹莓派開機後,用 raspi-config 的 expand filesystem 功能也可以自動擴張磁碟分割區的大小,善用未使用的空間。

參考資料:Softwarebakery