创建嵌入式linux混合文件系统(ramdisk+jffs2)

来源:互联网 发布:当数据库被破坏时,利用 编辑:程序博客网 时间:2024/06/11 14:52

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=16384mke2fs -F -v -m0 initrd.imgmount -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:)

1 0
原创粉丝点击