Jffs2文件系统的制作及使用

时间:2021-03-08 09:07:31

为什么使用Jffs2?

在嵌入式系统中,为加速启动,核心的根文件系统会使用RamFs,一般会放到RAM中,但是这种文件系统一般是掉电丢失的,因为存储在Ram中。但实际应用中有一些配置文件是需要非易失特性的,主要用于配置应用程序。因此,一般的嵌入式产品的文件系统是多种格式共存的,核心的Rootfs使用ramfs,可以加速系统运行速度,一些需要非易失性的配置文件一般使用Jffs2(norflash做为存储介质时)或者yffs2(nandflash做为存储介质时)文件系统。
Jffs2(Journalling Flash FileSystem Version2)文件系统专为嵌入式系统设计,存储介质主要是NorFlash或者NandFlash,是一种简单非易失性的文件系统。

分区是啥?

我们习惯将大的复杂的东西划分为小的简单的东西,就像你买个衣橱,会把衣橱按照功能划分成几个区域(有放上衣的、有放裤子的)。利用信息存储空间也一样,当你拿到一个256G硬盘的时候,你很自然地会按照功能分出几个空间来—–分区。一般会有安装OS的盘、软件的盘、资料盘、游戏盘。这样会有几个好处—易查找、故障隔离,好了,通俗地讲:好找资料、一个分区如果出了问题,一般不会影响到别的分区。硬盘的分区信息是被存储到硬盘的0磁道0柱面1扇区的DPT(Disk Partition Table硬盘分区表)。说的直白点就是画出几个范围,大家互相不干扰,这些分区作为OS格式化文件系统的最小单位。
Jffs2文件系统的存储介质一般是norflash,也可以划分分区,但这些分区并不保存到norflash上。只是BootLoader和kernel的之间的一种约定。嵌入式系统中的分区一般分为:u-boot、u-boot环境变量、kernel、dtb、rootfs、其他一些设计需要的分区。
说了这么多,来点实际的:
我的板子上有个128M的norflash,然后分了6个分区。
fe8000000.flash: Found 1 x16 devices at 0x0 in 16-bit bank. Manufacturer ID 0x000001 Chip ID 0x002801
Amd/Fujitsu Extended Query Table at 0x0040
Amd/Fujitsu Extended Query version 1.5.
number of CFI chips: 1
6 cmdlinepart partitions found on MTD device fe8000000.flash
Creating 6 MTD partitions on "fe8000000.flash":
0x000000000000-0x000006e00000 : "uboot"
0x000006e00000-0x000006e20000 : "kernel"
0x000006e20000-0x000006e40000 : "dtb"
0x000006e40000-0x000006e60000 : "jffs2"
0x000005000000-0x000005300000 : "test"
0x000005300000-0x000008000000 : "fs"

嵌入式系统下所有的norflash、nandflash等存储设备都被抽象成mtd存储设备。上面分区的u-boot对应系统下的dev/mtdblock0,test分区代表dev/mtdblock4。
fdisk -l |grep mtd

Disk /dev/mtdblock0: 110 MiB, 115343360 bytes, 225280 sectors
Disk /dev/mtdblock1: 128 KiB, 131072 bytes, 256 sectors
Disk /dev/mtdblock2: 128 KiB, 131072 bytes, 256 sectors
Disk /dev/mtdblock3: 128 KiB, 131072 bytes, 256 sectors
Disk /dev/mtdblock4: 3 MiB, 3145728 bytes, 6144 sectors
Disk /dev/mtdblock5: 45 MiB, 47185920 bytes, 92160 sector

分区有了,我们就可以制作文件系统了。
mkfs.mkfs.jffs2 /dev/mtdblock4
好像很明朗了,分区是制作文件系统的最小单位。就是说你可以把/dev/mtdblock3格式化成ext2格式的文件系统,同时把/dev/mtdblock4格式化成jffs2格式的文件系统。
下面的章节会详细说明制作jffs2文件系统并尽量说明白u-boot、kernel和文件系统的关系以及使用方法。

u-boot下的分区和Linux下的分区的联系和区别

从上面的描述看到,分区就说约定了怎么划分存储空间的一种说法而已。那就顺便带出了几个问题,u-boot下怎么划分分区?需要将分区信息存储吗?kernel怎么分区?u-boot下分区和kernel下分区有什么区别和联系呐?
u-boot下有时候也有分区的概念,但u-boot下的分区的概念和linux下不太一样,linux下主要是用于将一个block设备从物理这个层面上划分为几个分区,是系统格式化成不同文件系统的最小单元。
但在u-boot下只是一个地址范围,仅仅表示起始、结束地址而已。其实也差不多了,一样的目的只是表示的方法不一样罢了。

  1. 在代码里写死(此方法已经不使用,dts替代)。
  2. 使用dts文件创建分区。
  3. 在u-boot下使用cmdline传参(bootargs->mtdparts),且cmdline的优先级较高,即如果dtb设置了mtd 分区,但同时cmdline也设置了分区则,cmdline设置的分区会覆盖掉kernel设置的分区。以mtdparts设置的为最后的设置内容。

我们使用cmdline传参数的方法来做说明。
setenv bootargs "root=/dev/ram rw console=ttyS0,115200 ramdisk_size=70000000 mtdparts=fe8000000.flash:110m(uboot),128k(kernel),128k(dtb),128k(jffs2),3m@0x5000000(test),-(fs)"
使用环境变量mtdparts,具体说明见u-boot官网。
总之,u-boot里划分的存储设备分区和kernel需一致,这样在u-boot里升级固件会方便一些。因为我们在开发阶段,会使用HOST开发主机制作文件系统,然后烧录到指定的分区,kernel启动后可以mount该分区,从而读写文件。

怎么制作Jffs2文件系统?

制作jffs2文件系统很简单。
mkfs.jffs2 -r ./jaffs2_rootfs -o rootfs.jaffs2 -pad 0x300000 -b -n
具体参数 mkfs.jffs2 --help

使用Jffs2

我们得将制作好的jffs2格式的文件系统烧写到norflash上,烧录到那个分区?那个位置?客官别着急,我们看下我们的分区咋划分的,上面的mtdparts=tdparts=fe8000000.flash:110m(uboot),128k(kernel),128k(dtb),128k(jffs2),3m@0x5000000(test),-(fs)
我们计划使用test这个分区来做测试。可以看到test分区大小是3M,且起始地址在norflash的0x5000000的offset处。而且我们的flash存储映射到CPU线性地址0xfe8000000开始的地方,从CPU侧来看的线性地址为:0xfe8000000+0x5000000=0xfed000000。test这个分区的基地址=fe8000000,加上offset=0x5000000 因此地址=0xfed000000,其中在CPU侧来操作nor flash 时,操作的地址为0xed000000,0xfed000000为内部addr bus地址,该CPU是一个32bit指令集,32bit data bus,64bit address bus的IC,在指令集这一层面来看,操作的存储器地址32bit,CPU内部做移位。
启动系统后,可以查看test分区对应的/dev/mtdblock4。
fdisk -l /dev/mtdblock4
Disk /dev/mtdblock4: 3 MiB, 3145728 bytes, 6144 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

系统启动log里面test分区的range:
0x000005000000-0x000005300000 : "test"
对应到CPU那一侧的操作地址range:
0x0000ed000000-0x0000ed300000

好,烧录到那个位置知道了,下面开始烧录。
usb start;fatload usb 0 1000000 jaffs2_rootfs·
protect off 0xed000000 +$filesize;erase 0xed000000 +$filesize;cp.b 1000000 0xed000000 $filesize;protect on 0xed000000 +$filesize
不同开发环境的烧录方法可能不一样,但是大致思路是将生成的jffs2文件系统的image烧录到指定的offset上。
然后启动kernel,启动后:
mkdir test
mount -t jffs2 /dev/mtdblock4 test/

mount |grep jffs2
/dev/mtdblock4 on /home/root/test type jffs2 (rw,relatime)

df -h
Filesystem Size Used Avail Use% Mounted on
/dev/root 202M 83M 109M 44% /
devtmpfs 2.0G 4.0K 2.0G 1% /dev
tmpfs 2.0G 168K 2.0G 1% /run
tmpfs 2.0G 156K 2.0G 1% /var/volatile
/dev/sda1 3.5G 859M 2.6G 25% /run/media/sda1
/dev/mtdblock4 3.0M 388K 2.7M 13% /home/root/test

附录

  • dts 中的norflash分区

nor@0,0 {
#address-cells = <0x1>;
#size-cells = <0x1>;
compatible = "cfi-flash";
reg = <0x0 0x0 0x8000000>;
bank-width = <0x2>;
device-width = <0x2>;

    `   partition@0 {
reg = <0x0 0x20000>;
label = "RCW";
read-only;
};

partition@20000 {
reg = <0x20000 0x6e0000>;
label = "kernel";
};

partition@800000 {
reg = <0x800000 0x100000>;
label = "DTB";
};

partition@a00000 {
reg = <0xa00000 0x3540000>;
label = "File system";
};
};`
  • mtdpart 设置
    setenv bootargs "root=/dev/ram rw console=ttyS0,115200 ramdisk_size=70000000 mtdparts=fe8000000.flash:110m(uboot),128k(kernel),128k(dtb),128k(jffs2),3m@0x5000000(test),-(fs)"