Hi!大家好,我是CrazyCatJack。又和大家见面了。今天给大家带来的是构建Linux下的根文件系统。希望大家看过之后都能构建出符合自己需求的根文件系统^_^
1.内容概述
1.构造过程
今天给大家展示的根文件系统构造过程如下图所示:
正如大家看到的,这是一个环环相扣的过程。因为在这四个方面的内容其实相互包含,有很多交集的地方,所以我用环图给大家展示。在第一部分,我会给大家讲解如何在etc/目录下编写相应的配置文件,包含etc/init.d/rcS和etc/fstab等;在第二部分,将会教大家使用mdev,利用它来添加热插拔功能,自动创建已经存在和即将加入的设备驱动节点;第三部分将给大家详细讲解如何用工具生成两种文件类型的根文件系统,包含yaffs2和jffs2类型;第四部分给大家讲解如何用NFS挂载根文件系统,包含启动后挂载和启动前挂载两种方法。
2.最小根文件系统
今天我们构建的最小根文件系统启动过程如下:
这是我做的最小根文件系统的执行流程图。这其中2.3.4步是busybox的工作。关于busybox请参照我的上一篇博文《Linux根文件系统分析之init和busybox》,里面CCJ给大家详细讲解了2.3.4步的内容^_^ 。今天主要给大家讲解第一步和第五步。
1) 创建设备console和null:
首先,我们需要创建一个目录,自己命名。这里博主创建CCJ目录:mkdir CCJ。然后进入自己创建的这个文件夹目录,并创建dev目录:mkdir dev 。进入目录dev:cd dev/ 。然后我们需要查看一下console和null的主次设备号:ls /dev/console /dev/null -l 。这里博主显示的console的主设备号为5,次设备号为1;null的主设备号为1,次设备号为3。现在我们就可以创建设备console和null了:
mknod console c 5 1
mknod null c 1 3
在CCJ目录下创建etc目录:mkdir etc 。创建etc目录下的inittab配置文件:vi etc/inittab 。并在其中添加console::askfirst:-/bin/sh 使这个shell的标准输入,标准输出,标准错误都指向这个console。第一步就这样轻松的完成啦!
2)配置C库:
首先我们创建lib目录,这个目录将用来存放C库的文件:mkdir CCJ/lib 。进入你存放C库文件的目录下,将C库文件复制到CCJ/lib目录下:cp *.so* /CCJ/lib -d。这里CCJ将C库中的动态库文件及其链接关系都复制到了CCJ/lib目录下。这样配置C库也完成了。
2.构造过程
1.编写etc目录下的配置文件和mdev的使用
由于使用mdev同样要配置etc目录下的配置文件,所以CCJ将这两个过程合并为一个小题目一起给大家讲解^_^ 。
其实经过上面的过程,我们解决了根文件系统执行的第一步到第五步,现在已经可以生成一个小小的根文件系统了。但如果你生成并烧写到硬件上就会发现,这个小小的根文件系统的确能够正常运行,但它的功能还很有限。为了让它有更加丰富的功能和更方便的使用,现在我们需要添加一些新的配置。
此刻我们制作的根文件系统中有些程序还没有安装,比如ps程序。有两种方法,一种是在根文件系统启动后我们手动挂载proc:
mkdir proc
mount -t proc none /proc
这里的proc是linux内核提供的虚拟文件系统。它会将linux系统运行时的信息采集,必须将它挂接到/proc目录下。我们才能使用ps程序。那么经过这样的挂载我们就可以正常使用ps了。下面是在烧写了根文件系统的硬件上执行的命令:
ps # ps PID Uid VSZ Stat Command 1 0 3088 S init 2 0 SW< [kthreadd] 3 0 SWN [ksoftirqd/0] 4 0 SW< [watchdog/0] 5 0 SW< [events/0] 6 0 SW< [khelper] 55 0 SW< [kblockd/0] 56 0 SW< [ksuspend_usbd] 59 0 SW< [khubd] 61 0 SW< [kseriod] 73 0 SW [pdflush] 74 0 SW [pdflush] 75 0 SW< [kswapd0] 76 0 SW< [aio/0] 710 0 SW< [mtdblockd] 745 0 SW< [kmmcd] 763 0 3092 S -sh 770 0 3092 R ps
这里我们可以看到有许多进程,其中PID表示的是进程号。ps是用来查看后台进程工作情况的命令。这时,我们进入proc目录,随便打开一个进程查看:
# cd /proc/ # ls 1 745 diskstats locks sys 2 75 driver meminfo sysrq-trigger 3 76 execdomains misc sysvipc 4 763 fb modules timer_list 5 771 filesystems mounts tty 55 asound fs mtd uptime 56 buddyinfo interrupts net version 59 bus iomem partitions vmstat 6 cmdline ioports scsi yaffs 61 cpu irq self zoneinfo 710 cpuinfo kallsyms slabinfo 73 crypto kmsg stat 74 devices loadavg swaps # cd 1 # ls -l fd lrwx------ 1 0 0 64 Jan 1 00:10 0 -> /dev/console lrwx------ 1 0 0 64 Jan 1 00:10 1 -> /dev/console lrwx------ 1 0 0 64 Jan 1 00:10 2 -> /dev/console
正如大家看到的,proc/目录下有许多的进程。首列数字与上面的PID号对应,进入PID号,既是进入对应的应用程序。通过" ls -l fd "发现PID为1的进程其标准输入,标准输出,标准错误都指向/dev/console。
这种挂载虚拟文件系统的方法虽然可用,但是十分麻烦,如果不想自己一个一个手动挂载的话,可以在etc/下编写配置文件,统一处理。这样在根文件系统启动的时候,配置文件会帮我们处理好所有程序的文件系统挂载。我们就能直接使用各种程序了^_^ 。
在etc/inittab中添加::sysinit:/etc/init.d/rcS 。然后创建rcS这个脚本文件:mkdir etc/init.d vi etc/init.d/rcS。在其中输入:mount -t proc none /proc。保存后再给它添加可执行权限:chmod +x etc/init.d/rcS 。这样修改后的根文件系统就会在启动时为应用程序自动挂载proc,效果和上面手动挂载的一样。这里我们也可以在rcS文件中添加:mount -a 命令来取代mount -t 命令。mount -a命令是根据etc/fstab来挂载文件的。编写etc/fstab为:proc /proc proc defaults 0 0。其效果和上面相同。
由于linux下有很多设备驱动,如果手动一个一个添加dev/目录下的内容十分麻烦,所以我们可以用udev自动添加设备节点。这里我们用到的是busybox下的udev的简化版:mdev。那么我们该如何配置才能使用mdev呢?查看busybox目录下的mdev.txt文件,里面有详细的说明:
Mdev has two primary uses: initial population and dynamic updates. Both require sysfs support in the kernel and have it mounted at /sys. For dynamic updates, you also need to have hotplugging enabled in your kernel. Here‘s a typical code snippet from the init script: [1] mount -t sysfs sysfs /sys [2] echo /bin/mdev > /proc/sys/kernel/hotplug [3] mdev -s Of course, a more "full" setup would entail executing this before the previous code snippet: [4] mount -t tmpfs mdev /dev [5] mkdir /dev/pts [6] mount -t devpts devpts /dev/pts
首先,根据 [1]、[4]我们得知,需要sys目录,dev目录。且需要添加sysfs和tempfs文件类型。所以在etc/fstab中添加:
sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
这里从左至右依次代表:设备名、挂载点、文件系统类型、默认参数等。又根据[2]、[3]、[5]、[6]在etc/init.d/rcS中添加:
mkdir /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
这里的hotplug表热拔插,指向mdev。当添加新设备时,会调用它来创建新设备节点。而mdev -s则表示把现有的设备节点创建出来。经过这一番更改,我们重新生成根文件系统,烧写到硬件设备上,通过终端观察实验现象:
大家可以看到dev/目录下有很多设备节点,这都是mdev自动创建出来的。etc目录下配置文件和mdev使用讲解完毕。
2.生成yaffs2和jffs2文件
下面讲一下如何利用工具生成yaffs2和jffs2文件。因为我们在linux下构建的根文件系统是要烧写到硬件平台上的,所以需要生成一个可烧写的文件。今天将两种可烧写的根文件系统的文件类型:yaffs2和jffs2。
1>yaffs2:
首先我们需要从网络或其他途径获取到mkyaffs2image工具,利用它来生成yaffs2文件。进入存有mkyaffs2image工具的目录下。将它解压缩:
cd document_mkyaffs2/
tar xjf yaffs_source_util_larger_small_page_nand.tar.bz2
cd Development_util_ok
cd yaffs2/
cd utils/
进入工具目录后:make 。生成我们需要的mkyaffs2image。将它复制:cp mkyaffs2image /usr/local/bin/。并给它可执行权限:chmod +x /usr/local/bin/mkyaffs2image。再次进入我们的CCJ目录,利用mkyaffs2image生成可烧写的根文件系统:mkyaffs2image CCJ CCJ.yaffs2。
2>jffs2:
生成jffs2比yaffs2要稍稍复杂一些。CCJ这里用到的是"mtd-utils-05.07.23.tar.bz2",它是MTD设备的工具包,编译它就会生成制作jffs2的工具。它需要zlib压缩包,所以我们要先安装zlib。大家可以自行下载zlib的源码进行安装,这里举例说明:
tar xzf zlib-1.2.3.tar.gz
cd zlib-1.2.3/
./configure --shared --prefix=/usr/
make
这里的shared是编译动态库,prefix表示将zlib安装在哪里。make生成zlib后,make install进行安装。
现在有了zlib后,我们可以编译jffs2的生成工具了。
tar xjf mtd-utils-05.07.23.tar.bz2
cd mtd-util-05.07.23/util
make
make install
现在我们就安装了解压后的jffs2生成工具。进入CCJ目录,执行:mkfs.jffs2 -n -s 2048 -e 128KiB -d CCJ -o CCJ.jffs2
这里的-n表示不要在每一个擦除块上加上清楚标志;-s 2048表示一页大小为2048字节;-e 128KiB表示一个擦除块大小为128KB;-d 表根文件系统目录;-o表示输出文件。按理说,执行完这步生成的jffs2烧写到硬件平台上就能够正常运行,但是请大家注意更改自己的bootloader的环境变量,更改为挂载jffs文件类型,而不要默认为yaffs,否则会启动失败。
3.NFS挂载根文件系统
这里也给大家介绍两种挂载方法:1.是在已经启动的硬件平台上,将服务器根文件系统挂载到硬件平台上。2.是在未启动的硬件平台上,通过修改环境变量,指定挂载地址为服务器地址,系统一启动就挂载服务器的根文件系统。
1>启动后挂载:
要想用NFS挂载根文件系统,必须满足两个条件:(1)服务器允许那个目录可被挂接。(2)硬件平台去挂接那个目录。首先是第一个条件,我们需要到etc/exports里去配置被允许挂接的目录:
cd CCJ
vi /etc/exports
在其中添加我们根文件系统所在目录:
保存后,重启NFS服务:/etc/init.d/nfs-kernel-server restart。理论上现在就可以挂载了,我们可以先试验一下,用服务器挂载自己的根文件系统看能否成功:
mount -t nfs 192.168.1.19:/work/nfs_root/CCJ /mnt
如果系统没有提示出错,而且根文件系统能够正常使用,就说明我们成功了。那么现在来真正的挂载,在已经启动的硬件平台的终端上,我们创建挂载点:mkdir /mnt。然后开始挂载:
mount -t nfs -o nolock 192.168.1.19:/work/nfs_root/CCJ /mnt
此时,如果挂载成功,在硬件平台上显示的就是服务器的根文件系统了,里面的文件内容一模一样,在服务器上对文件内容的更改也会显示在硬件平台上^_^ 。
2>启动前挂载:
这里,CCJ使用的bootloader是u-boot。只需在系统启动时,进入u-boot。修改u-boot的环境变量,使系统的根文件系统地址指向服务器的地址即可。在u-boot下,NFS挂载的定义格式如下:
nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>]
ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>
所以我们只需根据自己实际的IP地址、网关、子网掩码等替换掉其中的参数,然后将这条命令输给u-boot并保存就可以了。这里CCJ的硬件平台IP=192.168.1.13;服务器IP=192.168.1.19;网关=192.168.1.1;子网掩码=255.255.255.0 。在u-boot下,输入print就可以看到当前的环境变量,其中和挂载根文件系统相关的是bootargs:
bootargs=noinitrd root=/dev/mtdblock3 rootfstype=jffs2 init=/linuxrc console=ttySAC0
现在根据挂载跟文件系统的格式,我们需要更改:
set bootargs noinitrd root=/dev/nfs nfsroot=192.168.1.19:/work/nfs_root/CCJ ip=192.168.1.13:192.168.1.19:192.168.1.1:255.255.255.0::eth0:off rootfstype=jffs2 init=/linuxrc console=ttySAC0
将这条环境变量保存,然后启动内核。就可以看到我们的根文件系统和服务器是一样的,也就是说挂载到了服务器的根文件系统上 。
敬告: 本文原创,欢迎转载^_^ 转载请在文章醒目处注明: 原创作者ID: CrazyCatJack |
题外话:
马上就是2017年了,首先祝大家元旦快乐!^_^ 这一年里,CCJ收获了很多。完成了为期一年的项目,做出了满意的机器人;找到了心仪的工作,马上就要到广东去做linux驱动开发了,从来没有到过这么往南的地方呢;相信大家也收获良多吧,祝我们新的一年有更多的成长,世界也会因代码而不一样吧,嘿嘿~
CrazyCatJack
2016-12-31 16:06:18