根文件系统的启动流程
- 打开/dev/console,并执行sys_dup(0),sys_dup(0),即将标准输入输出错误重定位到console终端,而该console也是串口0的输出。
- run_init_process为跳转指令,只要执行了就不再返回。若用户指定了execute_command=xxx则执行run_init_process(execute_command),一般指定为linuxrc文件,若未指定或出错依次判断执行/sbin下的init程序、etc/init、/bin/init、/bin/sh文件,都没有则启动失败。
- 通过busybox构建init文件。
- 读取和解析init文件的配置文件,根据配置文件启动用户程序
BUSYBOS源码分析
busybox -> init_main
parse_inittab
fopen(INITTAB,"r");//打开etc/inittab配置文件
new_init_action;//创建了多个init_action结构存入链表中
run_actions;//根据时机执行链表中的程序
相关函数解析:
inittab的格式:<id> : <runlevels> : <action> : <process>
id -> /dev/id ,用作终端:stdin,stdout,stderr:ptintf,scanf,err
runlevels:忽略
action:执行时机
process:指定用户程序或脚本
new_init_action(action,process, id);
创建一个init_action结构,填充
把这个结构放入init_action_list链表
run_actions:执行一系列时机的用户程序
run_actions(SYSINIT);
waitfor(a,0); //执行应用程序,等待他结束
->run(a); //创建子进程
->waitpid(runpid, &status, 0); //等待子进程结束
delete_init_action(a); //从init_action_list链表中删除
run_actions(WAIT);
waitfor(a,0); //执行应用程序,等待他结束
->run(a); //创建子进程
->waitpid(runpid, &status, 0); //等待子进程结束
delete_init_action(a); //从init_action_list链表中删除
run_actions(ONCE);
run(a); //创建子进程
delete_init_action(a); //从init_action_list链表中删除
while(1){
run_actions(RESPAWN);
if(a->pid == 0){
a->pid = run(a);
}
run_actions(ASKFIRST);
if(a->pid == 0){
a->pid = run(a); //打印:Please press Enter to activate this console,并等待回车,创建子进程
}
wpid = wait(NULL); //等待子进程退出
while(wpid > 0){
a->pid = 0; //退出后设置pid=0
}
}
小结
构建最小根文件系统五要素:
- /dev/console /dev/null, null用于若未指定console则将标准输入输出错误定向到null中。
- init文件的构建—>busybox工具
- /etc/inittab 配置文件
- 配置文件中指定的程序
- C库,支持文件系统启动中的一系列文件操作等库函数。
BUSYBOX
- make menuconfig中配置指定编译工具,也可以在Makefile中修改
Busybox Settings —>
Build Options —>
(arm-none-linux-gnueabi-) Cross Compiler prefix
Build BusyBox as a static binary (no shared libs) //静态编译 - 在菜单中勾选上需要的支持系统命令,默认一般均配置好了
- make 编译
- make install 将编译后生成的文件安装到 _install目录下,也可以创建新目录,make CONFIG_PREFIX=/x/x install安装到指定目录中
- 生成bin linuxrc sbin usr 的子目录,已完成基本的linux系统常用命令与启动文件linuxrc
- 在_install目录下新建子目录:dev 、etc、mnt、proc、var、tmp、sys、root
- 将交叉工具链中的c库中的动态库的链接文件拷贝到_install目录下的lib目录。cp xxx/lib/* .so * . -d,-d表示拷贝链接文件,否则将拷贝原库文件,会非常大。
- 删除该lib目录下的静态库,.a后缀文件,或删除没用到的库,对其进行裁剪,一般尽量小于8M
- 将现成的文件系统中的/etc目录下配置文件和启动脚本拷贝到当前/etc下,也可以自行创建,第一个应用程序linuxrc会解析/etc目录下的配置文件和脚本,/etc下几个文件的分析:
1. inittab: " ::sysinit:/etc/init.d/rcS " 指定启动运行的脚本文件
2. init.d/rcS:mount挂载根文件系统的目录,创建ptc文件,即终端输入中的命令提示"[xxxx]:",支持热拔插,做一系列挂载文件系统后的初始化步骤。如果指定了mount -a 则依赖于fstab文件。
3. fstab:指定要挂载的目录
4. profile:指定提示符,指定环境变量(系统命令路径、库路径) - 在/dev目录下创建console与null设备结点:mknod dev/console c 5 1,对应串口输出的设备结点,mknod dev/console c 1 3。
手动制作/etc目录下的配置文件
目录example下有各文件编写格式的说明
- 在/etc目录下创建inittab配置文件,写入:
console::askfirst:-/bin/sh #指定输出终端 - 指定启动脚本,挂载内核proc文件系统(于内核进程相关),etc目录下创建init.d目录,并在此目录下创建rcS文件,写入:
mount -t proc none /proc,并且在配置文件中写入:::sysinit:etc/init.d/rcS,指定启动脚本。其中启动脚本中还有另一种方式挂载根文件系统:mount -a,该方法依赖于fastab文件。 - 利用mdev机制,自动创建/dev下的设备结点。
在fastab中添加以下语句:
proc /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
在rcS目录中继线添加以下语句:
mkdir /dev /pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug #支持热拔插,自动创建设备结点
mdev -s #将内核原先有的设备驱动结点都创建出来
inittab的格式:<id> : <runlevels> : <action> : <process>
id -> /dev/id ,用作终端:stdin,stdout,stderr:ptintf,scanf,err
runlevels:忽略
action:执行时机
process:指定用户程序或脚本
fastab的格式:device mount-point type options dump fsck order
制作ramdisk格式的根文件系统镜像
要将文件系统烧入开发板,需要做镜像文件
nfs rootfs:可读写,保持在内存中,用于开发调试
cramfs :只读,安全性高,保存在Nor/Nand,一般用于存放重要信息的区域
jffs2:可读写,保存在 Nor/Nand,日志文件系统,掉电不会丢失数据
yaffs1:可读性,小页512B,
yaffs2:可读写,大页2048B,保存在Nand,用在手机领域
ext2/ext3:可读写,保存在硬盘,用在电脑
ext2 over ramdisk:可读写,将内存虚拟为磁盘,将访问内存模拟访问文件,由于是访问内存方式则掉电数据即丢失。
一般都是混合起来使用,将不同数据存放于不同格式区中。
制作步骤:
- cd ~
- 制作一个8M的镜像。dd用来制作映像,if为input file,of为output file,bs为区块大小,count为区块数量,if=/dev/zero将镜像文件一开始全清零,然后of=ramdisk生成ramdisk映像。
dd if=/dev/zero of=ramdisk bs=1k count=8192 - 将ramdisk映像制作为ext2的文件格式,其中linux默认支持mkfs.ext2命令:
mkfs.ext2 -F ramdisk - 目前该文件系统镜像为空内容,需要将我们前面做的文件系统拷贝进去,一般是通过将ramdisk挂载到文件系统目录下:
在mnt目录下新建initrd目录,作为挂载点
mount -t ext2 ramdisk /mnt/initrd
将之前制作的文件系统_install目录拷贝到initrd目录下:cp xxx initrd -a,这里注意文件系统大小必须小于ramdisk的空间大小 - 压缩ramdisk文件
gzip --best -c ramdisk > ramdisk.gz - 格式化为uboot识别的格式
mkimage -n “ramdisk” -A arm -O linux -T ramdisk -C gzip -d ramdisk.gz ramdisk.img - 制作成功生成ramdisk.img镜像文件
若读者为tftp方式挂载根文件系统的话,将其拷贝的/tftpboot目录下
cp ramdisk.img /tftpboot/。修改uboot中的启动参数bootcmd:
setenv bootcmd tftp 41000000 uImage \;tftp 42000000 设备树.dtb\;tftp 43000000 ramdisk.img\;bootm 41000000 43000000 42000000
注意:setenv语句中不能直接以分号;进行语句拆分,它为特殊字符,需用转义符" \ "。