基於 SquashFS 構建 Linux 可讀寫文件系統


轉載:http://www.oschina.net/question/129540_116839

在當前的嵌入式操作系統開發中,Linux 操作系統通常被壓縮成 Image 后存放在 Flash 設備中。在系統啟動過程中,這些 Image 被直接掛載到根文件系統, 然而這時的根文件系統是只讀的, 用戶不能在這個文件系統中進行任何寫的操作。 如果把 Image 解壓后直接拷貝到內存中,也可以實現寫的功能,但是嵌入式系統一直存在內存大小方面的限制,所以將整個 Linux 系統拷入內存是不可取的。 本文將介紹一種直接掛載 Image 到根目錄下,同時實現文件系統可讀寫的功能。

 

嵌入式 Linux 啟動過程

本文所描述的的 Linux Image 由 BootLoader、kernel、initrd、rootfs 組成,它們共同存在於一個可以啟動的存儲設備中(本文以 USB 為例)。組成架構如下:

圖 1. 可啟動 linux 鏡像文件結構 
aaa 

各個模塊的作用如下:

  • Boot Loader:由 BIOS 加載,用於將后續的 Kernel 和 initrd 的裝載到內存中
  • kernel:為 initrd 運行提供基礎的運行環境
  • initrd:檢測並加載各種驅動程序
  • rootfs:根文件系統,用戶的各種操作都是基於這個被最后加載的文件系統

其調用順序是 Boot Loader->kernel->initrd->rootfs。

當機器上電時首先 BIOS 會啟動,然后裝載 USB 設備中的 Boot Loader、kernel,、nitrd 到內存中,由於這些文件大小總和小於 10M,所以我們直接拷貝到內存中再執行不會有問題。

最后要加載的 rootfs 是用戶最終進行讀寫操作的文件系統。

  • 在非嵌入式系統中,這部分文件通常儲存在可直接讀寫的硬盤上,因此直接掛載到根目錄后(例如:mount /dev/sda1 /mnt)就可以進行讀寫操作。
  • 在嵌入式系統中,它是一個壓縮的文件系統,大小通常是好幾百兆,解壓后的大小都超過 1G,如果直接 mount 到系統目錄,那么系統目錄是只讀的,不可進行寫入操作。而如果把它加壓到內存中可以實現讀寫的操作,但是這么大的文件直接解壓到內存中對於嵌入式設備來說 是不可接受的。因此我們需要找到一種不拷貝 rootfs 到內存中,同時又可以對最終的根文件系統進行讀寫的方法。

只讀式壓縮文件系統介紹

在嵌入式的環境之下,內存和外存資源都需要節約使用。如果使用 RAMDISK(把內存當作 disk)方式來使用文件系統,那么在系統運行之后,首先要把外存 (Flash) 上的映像文件解壓縮到內存中,構造起 RAMDISK 環境,才可以開始運行程序。但是它也有很致命的弱點。在正常情況下,同樣的代碼不僅在外存中占據了空間 ( 以壓縮后的形式存在 ),而且還在內存中占用了更大的空間 ( 以解壓縮之后的形式存在 ),這違背了嵌入式環境下盡量節省資源的要求。以下兩種方案的誕生就是為了解決這個問題:

CramFS

CramFS 文件系統是專門針對閃存設計的只讀壓縮的文件系統,它並不需要一次性地將文件系統中的所有內容都解壓縮到內存之中,而只是在系統需要訪問某個位置的數據的 時侯,馬上計算出該數據在 CramFS 中的位置,將其實時地解壓縮到內存之中,然后通過對內存的訪問來獲取文件系統中需要讀取的數據。CramFS 中的解壓縮以及解壓縮之后的內存中數據存放位置都是由 CramFS 文件系統本身進行維護的,用戶並不需要了解具體的實現過程,因此這種方式增強了透明度,對開發人員來說,既方便,又節省了存儲空間。

SquashFS

SquashFS 也是一個只讀的文件系統,它可以將整個文件系統壓縮在一起,存放在某個設備,某個分區或者普通的文件中。如果您將其壓縮到一個設備中,那么您可以將其直接 mount 起來使用,而如果它僅僅是個文件的話,您可以將其當為一個 loopback 設備使用。

更多信息請參考“SquashFS”和“CramFS”。

本文主要介紹基於 SquashFS 的可讀寫文件系統構建。

Squash 壓縮文件系統的創建

"下載並安裝 Squash 工具"

步驟 1:創建空的根文件系統,文件系統的大小為 65536 × 24000/1024/1024=1.5G。接下來我們會在這個空的根文件系統中存放文件。

dd: 讀取源文件的內容並創建一個新文件,if 指定源文件內容,of 指定新文件名字,bs 和 count 指定新文件的大小 
mke2fs: 將新創建的 rootfs 格式化為 Linux 可識別的文件系統


清單 1. 創建空的根文件系統 

1 dd if=/dev/zero of=rootfs bs=65536 count=24000
2 mke2fs -F rootfs

步驟 2:掛載空的根文件系統,將 1.5G 的文件系統掛載在 mnt 目錄下,然后通過 mnt 目錄將內容寫入根文件系統。 

清單 2. 掛載空的根文件系統

1 mkdir mnt
2 mount rootfs mnt -o loop

步驟 3:拷貝根文件目錄的內容到文件系統 

清單 3. 拷貝根文件目錄統

1 cp -rp yourRootDir mnt
2 umount mnt

拷貝完后根文件系統的內容,如圖 2 所示: 

圖 2. 根文件系統內容 
aaa 


步驟 4:完成根文件系統的創建,這時的 rootfs 沒有被壓縮,接下來我們用工具將其壓縮成 Squash 格式的文件系統

mksquashfs-4.1 是在安裝 Squash 工具的過程中生成的命令,用於將一個文件夾下的內容打包並壓縮成一個文件系統。其后第一個參數為文件夾,第二個參數為生成的文件系統。


清單 4. 創建根文件系統

1 mkdir squashfs-dir
2 mv rootfs squshfs-dir
3 mksquashfs-4.1 squashfs-dir squashRootfs

到這里我們就完成了 Squash 壓縮文件系統的創建。接下來我們將討論如何在 Linux 啟動的過程中加載這個壓縮文件系統。

 

 

加載壓縮文件系統所使用的工具

在加載壓縮文件系統之前,我們需要確定您的 Linux 內核支持這種文件系統。 Device mapper 是 Linux 2.6 內核中提供的一種從邏輯設備到物理設備的映射框架機制,在該機制下,用戶可以很方便的根據自己的需要制定實現存儲資源的管理策略。

確保在 initrd 中已經集成“device-mapper

加載 Squash 壓縮文件系統

可讀寫文件系統原理如圖 3 所示:

圖 3. 可讀寫文件系統原理 
aaa 

squashRootfs 里面存儲了我們原始的根文件系統,我們在根文件系統中所有的寫操作會直接寫入 cowfile.out(cow:copy-on-write), 當我們讀取根文件系統時,如果讀取的內容沒有變化,將直接從 squashRootfs 中讀取,如果讀取的內容被更新過,將從 cowfile.out 中讀取。cowfile.out 文件的大小一般要比 squshRootfs 小,如果 cowfile.out 被寫滿,根文件系統的讀寫操作將會出錯,因此有必要給 cowfile.out 設置一個合理的大小以防止被寫滿。

由於 rootfs 是被 initrd 加載的,因此我們需要在 initrd 里面加入裝載 rootfs 的代碼。initrd 整個的執行過程是調用 /sbin/init 這個腳本文件,因此我們在這個腳本的最后加入以下代碼邏輯即可。

1. dm-mirror 為 device mapper 工作所需要的驅動 
2. mknod: 創建一些設備文件,參數 1:設備文件名 參數 2:設備類型 參數 3:主設備號 參數 4:次設備號 
3. 將 squashRootfs 通過 loop 設備的形式掛載在目錄 /realroot/mnt/ 下 
4. 將 /realroot/mnt/rootfs 設置為 loop 設備,並和 /dev/loop1 綁定 
5. "|"之前的部分是構建 dmsetup 的參數,其中 $(blockdev --getsize /dev/loop1) 表示創建鏡像文件的大小,/dev/loop1 /dev/loop2 表示鏡像文件是以 /realroot/mnt/rootfs 和 /realroot/mnt/cowfile.out 為藍本進行創建的(在前面的操作中 loop1 和 loop2 分別進行了綁定操作)


清單 5. 構建可讀寫文件系統腳本

01 # 加載驅動 ,參見注釋 1 modprobe dm-mirror
02  
03 # 創建設備 ,參見注釋 2 mknod /dev/zero c 1 5
04 mknod /dev/loop0 b 7 0
05 mknod /dev/loop1 b 7 1
06 mknod /dev/loop2 b 7 2
07 mkdir /dev/cow
08 mknod /dev/cow/ctl b 241 255
09 mknod /dev/cow/0 b 241 0
10  
11 # 掛載 squash 根文件系統,掛載完后您可以在 /realroot/mnt/ 下找到 rootfs 文件 ,參見注釋 3 mount /realroot/mnt/squashRootfs /realroot/mnt/ -o loop
12  
13 # 設置 rootfs 為 loop 設備 ,參見注釋 4 losetup /dev/loop1 /realroot/mnt/rootfs
14  
15 # 創建 cowfile.out 並掛載為 loop 設備,我們將來的寫操作都會寫入 cowfile.out
16 dd if=/dev/zero of=/realroot/mnt/cowfile.out bs=2K count=137500
17 losetup /dev/loop2 /realroot/mnt/cowfile.out
18  
19 # 將 /realroot/mnt/rootfs 和 /realroot/mnt/cowfile.out 結合起來創建一個邏輯根文件設備,
20 # 設備文件為 /dev/mapper/root_fs,,參見注釋 5 echo "0 $(blockdev --getsize /dev/loop1) snapshot /dev/loop1 /dev/loop2 p 64" |
21 dmsetup create root_fs
22  
23 # 將上面創建的邏輯根文件設備 /dev/mapper/root_fs 掛載就可以看到一個可讀寫的根文件系統
24 mount /dev/mapper/root_fs /realroot/mnt/Image
25  
26 # 切換到最終可讀寫的根文件系統
27 cd /realroot/mnt/Image
28 chroot ./sbin/init -i

加下來您就可以看到 rootfs 的所有內容,如圖 4 所示: 
圖 4. 被掛載的根文件系統內容
aaa 

還可以在這個文件系統中進行寫操作,如圖 5 所示:

圖 5. 根文件系統的寫操作
aaa 

最重要的是 rootfs 沒有被拷貝到內存中。

結束語

由於篇幅的限制,本文只給出了基本的描述。希望有更一步了解的讀者可以通過對以下 linux 命令的學習來深入了解。

構建 Squash 壓縮文件系統構所使用的主要命令:

  • mksquashfs:創建 Squash 壓縮文件系統

使用 Squash 壓縮文件系統構所使用的主要命令:

    • mknod:創建 Squash 壓縮文件系統
    • losetup:設置並控制 Loop 設備
    • chroot:改變根目錄
    • dmsetup:低水平邏輯卷管理


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM