1、 Ramdisk文件系統
Ramdisk就是將內存中的一塊區域作爲物理磁盤來使用的一種技術,內存盤的存取速度要遠快於目前的物理硬盤,所以它具有讀寫速度高的優勢。在嵌入式設備中,我們可以把Ramdisk與通常的NAND分區(如/dev/mtdblockN)同等對待來使用。但是不同的是Ramdisk文件系統不適合作爲長期保存文件的介質,掉電後修改的內容會隨內存內容的消失而消失。
2、 jffs2文件系統
它是在閃存上使用非常廣泛的讀/寫文件系統,在嵌入式系統中被普遍的應用。JFFS2 是一個日誌結構(log-structured)的文件系統,包含數據和原數據(meta-data)的節點在閃存上順序的存儲。JFFS2 之所以選擇日誌結構的存儲方式,是因爲對閃存的更新應該是out-of-place 的更新方式,而不是對磁盤的in-place 的更新方式。
平時我們的嵌入式產品基本上需要應用程序動態存儲的內容很少,大多都是一些程序的配置文件。所以需要操作的NAND Flash的空間並不需要很多,一般10多M基本上就足夠了。上面提到的兩種文件系統中,一個有速度但不能存儲,另一個可以存儲但效率不高,如何能在效率和存儲之間找到平衡點,魚和熊掌能否兼得?今天我們介紹的Ramdisk+jffs2混合文件系統就很好的解決了上面的問題,既可以將內存作爲文件分區快速操作又可以對NAND Flash操作實現文件的存儲。其實這種混合的文件系統已經存在很久了,例如很多視頻監控設備上就有類似的系統,像海康威視的文件系統就是ramdisk+ext2(不過一直沒有想明白爲什麼要用ext2這種適合硬盤分區的文件系統)、海思的視頻方案也大都使用的Ramdisk+jffs2文件系統。
3、基本原理:
這種混合的文件系統其實原理很簡單,雖說是混合的文件系統,但是並非同時存在兩套文件系統,而是以ramdisk爲真正的文件系統,jffs2系統只能算是一個jffs2格式化的NAND數據分區而已。通常是啓動ramdisk文件系統後通過mount指令掛載jffs2的數據分區到ramdisk文件系統的某個目錄下,就像我們掛載U盤一樣,這樣就可以通過掛載的目錄讀寫操作jffs2分區實現數據的存儲了。通常jffs2分區裏面我們存放將要執行的應用程序和配置文件,這樣做還有一點好處就是將ramdisk製作成一個基本的文件系統,不同項目改動的只是jffs2數據分區,可以實現文件系統的重複利用,就像我們經常見到的開發板,都是由一個核心板和一個底板組成,需要改變外設只需修改底板就可以了,核心板都是通用的。
於是根據上面的原理我們可以將NANDFlash重新分區爲如下圖所示:
一共7個分區,當然最上面的兩個也可以合爲一個,視項目情況而定。簡單介紹一下:
Xloader:我用的是TI的芯片,這是一個簡單的引導程序。
Bootloader:這個大家都熟悉,常用的是u-boot,這裏也是用的u-boot。
Params:存放u-boot啓動參數和內核引導參數,例如比較重要的兩個參數bootcmd和bootargs。
Kernel:linux內核。
Ramdisk:ramdisk文件系統分區。
Data:jffs2格式的數據分區。
Other:其他
NAND Flash分區修改可以通過修改內核中arch/arm/match-xxx/board-youboard.c文件中的NAND分區表,如下是我的NAND分區:
static struct mtd_partitionam3517evm_nand_partitions[] = {
/* All the partition sizes are listed interms of NAND block size */
{
.name ="xloader-nand",
.offset = 0,
.size = 4*(SZ_128K),
.mask_flags = MTD_WRITEABLE
},
{
.name ="uboot-nand",
.offset =MTDPART_OFS_APPEND,
.size = 15*(SZ_128K),
.mask_flags = MTD_WRITEABLE
},
{
.name ="params-nand",
.offset = MTDPART_OFS_APPEND,
.size = 1*(SZ_128K)
},
{
.name ="linux-nand",
.offset =MTDPART_OFS_APPEND,
.size = 32*(SZ_128K)
},
{
.name = "ramdisk-nand",
.size = 64*(SZ_128K),//8M
.offset = MTDPART_OFS_APPEND,
},
{
.name = "data-nand",
.size = 128*(SZ_128K),//16M
.offset = MTDPART_OFS_APPEND,
},
{
.name ="other-nand",
.size = MTDPART_SIZ_FULL,
.offset =MTDPART_OFS_APPEND,
},
};
4、啓動方式
啓動時uboot需要先把kernel加載到內存,然後再將ramdisk加載到內存,注意兩個地址不能有交集(另外也可以直接將ramdisk編譯到內核裏面,只需加載內核即可,爲了更容易理解,這裏就不對這種方式進行介紹了,但原理是一致的)。內核啓動時會通過讀取uboot傳遞給它的bootargs參數來判斷ramdisk系統的地址和大小,這樣就會去該地址掛載文件系統了。ramdisk啓動之後會執行文件系統的配置文件和腳本,在配置文件裏面我們可以設置jffs2系統分區的掛載,例如我是設置在/etc/profile文件裏面,用戶登錄後就會執行該配置文件,從而通過執行mount指令動態的掛載jffs2分區。
5、文件系統製作方法
如何製作Ramdisk文件系統網上有很多參考,大家可以自己去查找,或參考如下文章,這裏就不贅述了。
參考:《Ramdisk文件系統的製作-V0.2》、《Linuxinitial RAM disk (initrd) overview》
這裏要說的是嵌入式文件系統製作一般離不開busybox工具,網上也有很多介紹,新的版本基本上編譯不會出現什麼問題,我這裏用的是busybox-1.21.1版本。
這裏想解釋一下網上很多介紹busybox製作文件系統的資料都有許多共同的錯誤或者是誤解的地方。
第一點,GeneralConfiguration-> Don't use /usr選項,網上有人說要選擇這個選項,要不然工具會安裝到你係統的/usr目錄下面。其實大家通過看help可以發現並不是這個意思
Disable use of /usr. busybox --install and"make install" will install appletsonly to /bin and /sbin, never to /usr/bin or /usr/sbin.
你選擇這項的話,就只把工具生成到/bin和/sbin目錄下,若不選擇則同時還會分類生成到/usr/bin和/usr/sbin中。
第二點,網上大部分資料都會要求你將Build Options->[ ]Build BusyBox as a static binary (no shared libs)選項選中,避免出現問題。其實這個選項就是一個靜態編譯和動態編譯的選擇,靜態編譯會將用到的庫函數一塊編譯到文件中,但是體積稍大。動態編譯則不會,程序啓動的時候會動態加載庫文件,故而體積較小。只要將交叉編譯工具鏈的庫文件拷貝到文件系統中,即使動態編譯也不會有問題。經過本人的測試,選擇該項編譯後的busybox文件大小爲1.9M,不選擇該項編譯的busybox爲888K,均可正常使用。兩者文件大小差別不大,所以推薦還是選擇該項。
Busybox配置方面還有兩個重要的地方,
一個是你的交叉編譯工具鏈的配置:Build Options-> CrossCompiler prefix。
還有一個是InstallationOptions->BusyBox installation prefix裏面配置你要make install的安裝目錄。
剩下的就是選擇你需要的工具了,如果存儲空間充足的話,基本上按照默認來配置就可以了。
然後進行編譯make& make install,成功編譯後就會發現在安裝目錄下生成了幾個文件夾。根據生成的這些目錄就可以進行根文件系統的製作了。
除此之外你還需要創建如下目錄:/dev /home /mnt /tmp /var/boot /etc /lib /media /proc /sys等。
其中我個人認爲比較重要的目錄有lib、etc、dev三個,這三個目錄裏面的內容將直接決定文件系統能否正常啓動以及啓動的方式和服務的加載。
lib:這個大家都熟悉,就是linux系統下存放庫文件的地方,這個我們需要將交叉編譯鏈的庫文件拷貝到此處,使用cp指令時記得加上-d參數保持庫文件的鏈接關係。(這裏只介紹基本的文件系統,所以如果大家的程序用到其他的庫文件也應該拷貝到系統中,建議放在/usr/lib下面)。
etc:這裏存放的是系統啓動的配置文件,管理系統的相關進程的啓動、用戶登錄管理、網絡管理等。比較重要的文件有inittab、init.d/rcS、profile等,裏面東西很多,這裏以後會專門另寫一篇文章介紹,這裏就不再詳解了。如果想在用戶登錄後就自動加載data分區的話,可以在profile文件裏面添加上mount -t jffs2 /dev/mtdblock5/home/指令,這樣登錄到系統後就會把data分區自動掛載到/home目錄下了,所有對/home目錄下的文件操作都會保存到data分區(jffs2)裏面,當然也可以等到系統啓動後手動掛載該分區。
dev:存放系統驅動節點的目錄,系統啓動後會在此生產驅動設備節點,提供給上層應用程序同驅動交互。因爲一般系統啓動需要串口來做控制檯,所以需要提前手動創建串口驅動節點,不然系統啓動會報錯。下面是我事先創建的設備節點:
以上工作完成後一個基本的根文件系統就完成了,建議先用nfs掛載一下,看看能否正常啓動。如果沒有問題就可以給文件系統打包了,下面是我打包用的腳本。
dd if=/dev/zero of=initrd.img bs=1kcount=16384
mke2fs -F -v -m0 initrd.img
mount -o loop initrd.img /mnt/tmp/
cp -avd rootfs/* /mnt/tmp/
umount /mnt/tmp/
gzip -9 initrd.img
大家可能需要修改地方有:
initrd.img:打包後的ramdisk鏡像名稱
bs: block size,塊大小是1k。
count: 16384, 16k。所以文件系統的大小爲bs * count = 16M
/mnt/tmp/:一個臨時掛載目錄,這個是你編譯的主機上的目錄
rootfs/:存放的是你要打包的根文件系統。
把腳本和rootfs放在同一個目錄下執行就可以生成initrd.img文件了。
創建Data鏡像文件(可讀寫的jffs2的分區):
創建一個data目錄,然後把需要讀寫操作的文件拷貝其中,例如配置文件和可執行程序等。
打包data分區:
mkfs.jffs2 -s 0x800 -e 0x20000 -p 1000000 -d data/ -o data.jffs2 –n
-s:pagesize,nandflash的頁大小,我的nand頁爲2K
-e:eraseblock,nandflash的塊大小,塊爲128K
-p: 我理解爲要分區的尺寸大小,我這裏設置爲16M。
除此之外還要保證內核支持jffs2的文件系統,所以要將內核中如下配置選中:
-> File systems
-> Miscellaneous filesystems(MISC_FILESYSTEMS [=y])
-> <*> Journalling Flash FileSystem v2 (JFFS2) support
經過上面的步驟之後,兩塊不同格式的分區鏡像就做好了。
6、燒寫鏡像
將Xloader、uboot、kernel、ramdisk、data按照上面內核中的分區地址分別燒寫到NAND的相應位置,注意在使用uboot燒寫data鏡像的時候應該使用nand write.jffs2 指令(但是我用的nand write.i指令燒寫的貌似也沒出現問題,沒有具體研究原因)。
7、啓動參數
啓動時應先配置uboot的啓動參數,主要是bootcmd和bootargs兩個參數,bootcmd參數是告訴uboot應該以什麼樣的方式加載內核和文件系統,bootargs則是傳遞給內核,告訴內核以什麼樣的方式來啓動加載文件系統。
下面是我的啓動參數:
Uboot會先將內核加載到內存的80300000地址,然後將ramdisk的根文件系統加載到內存的81000000地址,然後從80300000地址啓動,也就是啓動內核。
bootcmd=nand read.i 80300000 280000300000;nand read.i 81000000 680000 440000;bootm 80300000
bootargs參數會告訴內核啓動的時候控制檯是串口ttyS2設備,這個要配置爲目標板的串口設備,如果錯了不會有打印信息的。root=/dev/ram0文件系統設備爲ram0,initrd=0x81000000,16M這裏告訴內核ramdisk加載的內存地址和大小。
bootargs=console=ttyS2,115200n8 root=/dev/ram0rw initrd=0x81000000,16M
如果一切正常的話你就會得到ramdisk+jffs2的混合文件系統了,通過df –h指令可以看到系統分區情況。
通過mount指令也可以看到具體的文件系統掛載的情況。
8、總結
就像剛開始說的那樣這種混合的文件系統早已存在並應用在嵌入式產品中了,前兩年在研究海康DVR產品的時候偶然發現了這種系統,但是受困於本人技術知識積累,而且網上也沒有相關的資料,所以一直沒有悟出其中的原理,現在終於得以實現,遂將研究過程敘以文字告知於衆,奈何書讀甚少,恐有文不達意之處,還請見諒。拋磚引玉,請列位大牛賜教!
PS:把我做好的ramdisk的源文件傳上來了,需要2個資源分,大家可以選擇下載,作爲一個參考。如果你的CPU也是cortex-A8架構的應該可以直接拿過來使用,如果是其他架構的,可以把/lib裏面庫文件替換爲你的交叉編譯工具的庫,然後把bin裏面busybox文件替換爲你生成的文件即可,例外還需注意要將/etc/inittab文件中的ttyS2::respawn:/bin/login替換成你的ttyXX串口設備的名稱。文件系統默認的用戶名是:root,密碼是:xxxxxx(6個‘x’)。如果正常啓動大家就會看到下面的啓動信息了。
下載地址:
另外還有一個我個人覺得挺好的資料,有關於uboot的啓動參數配置方面的。
可以google《Booting Linux kernel using U-Boot》,也可以通過下面鏈接下載:
Hope you enjoy:)