构造根文件系统
构造根文件系统
一、流程分析
回顾Linux内核启动流程
中提到的流程:
start_kernel:
中断初始化、时钟初始化、打印内核版本信息
处理uboot传进来的启动信息
处理命令行信息
调度初始化等各种初始化
挂接根文件系统
rest_init
创建
kernel_init
线程
早期的用户空间初始化
prepare_namespace
- 挂载根文件系统
mount_root
初始化某些设备
init_post
打开
/dev/console
设备标准设备设置
1
2 (void) sys_dup(0);
(void) sys_dup(0);执行应用程序
1
2
3
4
5
6
7
8
9
10
11 if (execute_command) {
run_init_process(execute_command);
printk(KERN_WARNING "Failed to execute %s. Attempting "
"defaults...\n", execute_command);
}
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
panic("No init found. Try passing init= option to kernel.");
发现内核有如下现象:
-
打开设备
/dev/console
(即终端,在开发板上为串口0),并将标准输入输出错误信息指向这里 -
如果uboot传入的命令行参数(bootargs)中有init参数时,会执行该程序,通常该程序不会返回
init参数最后会赋值给execute_command,即上述代码的第1行
通常,init=/linuxrc,实际上是链接到了busybox
-
如发生意外,会依次查找以下程序并执行(通常程序不会返回):
- /sbin/init
- /etc/init
- /bin/init
- /bin/sh
-
若上述程序全部执行失败,则输出
No init found. Try passing init= option to kernel.
二、init进程分析
busybox是各种常用命令的组合,一般来说常见命令都是通过链接来间接调用busybox并传入相关参数。如:ls
实际是busybox ls
通过上述分析,最后会进入busybox中的init
程序,经过搜索,得出会进入busybox中的init_main函数:
-
init_main
-
根据内核中的设置初始化控制台(sys_dup已经指定)
-
parse_inittab
-
打开
/etc/inittab
配置文件,若无则选取默认项详细格式见:busybox/examples/inittab.txt
: : :
:会自动加上/dev/前缀并用于终端(stdin、stdout、stderr),可省略 :忽略 :何时执行,包括: sysinit
,respawn
,askfirst
,wait
,once
,restart
,ctrlaltdel
,shutdown
:应用程序或脚本 默认项反向解析后得出配置文件:
1
2
3
4
5
6
7
8::ctrlaltdel:reboot
::shutdown:umount -a -r
::restart:init
::askfirst:-/bin/sh
tty2::askfirst:-/bin/sh
tty3::askfirst:-/bin/sh
tty4::askfirst:-/bin/sh
::sysinit:/etc/init.d/rcS(初始化脚本文件,其他相关的配置可以在这这个脚本中运行) -
解析配置文件
-
调用
new_init_action
函数1
static void new_init_action(int action, const char *command, const char *cons)
action:脚本中的
,执行时机 command:脚本中的
,应用程序或脚本 cons:脚本中的
,用作终端 - 创建一个
init_action
结构存储action、command、cons等数据 - 将该结构放入
init_action_list
链表
- 创建一个
-
-
调用
run_actions
函数一次执行脚本中参数所代表的一类应用程序或脚本 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19/* Now run everything that needs to be run */
/* First run the sysinit command */
run_actions(SYSINIT);//首先执行SYSINIT类应用程序或脚本
/* Next run anything that wants to block */
run_actions(WAIT);//接着执行WAIT类应用程序或脚本
/* Next run anything to be run only once */
run_actions(ONCE);//再执行ONCE类应用程序或脚本
/* Now run the looping stuff for the rest of forever */
while (1) {
/* run the respawn stuff */
run_actions(RESPAWN);//执行RESPAWN类应用程序或脚本
/* run the askfirst stuff */
run_actions(ASKFIRST);//执行ASKFIRST类应用程序或脚本
/* Wait for a child process to exit */
wpid = wait(NULL);
while (wpid > 0) {
......
}
}SYSINIT、WAIT类:
- 从之前的init_action_list链表中取出对应的项
- 通过
waitfor
函数运行对应应用程序或脚本- 调用run函数创建子进程运行对应应用程序或脚本
- 通过waitpid函数等待子进程结束
- 调用
delete_init_action
从链表中删除
ONCE类:
- 从之前的init_action_list链表中取出对应的项
- 调用run函数创建子进程运行对应应用程序或脚本
- 调用
delete_init_action
从链表中删除
RESPAWN类:
- 调用run函数创建子进程运行对应应用程序或脚本
- 子进程退出后再次调用run函数
ASKFIRST类:
- 调用run函数创建子进程运行对应应用程序或脚本
- 子进程退出后打印:
Please press Enter to activate this console
,并等待回车 - 再次调用run函数
总结
最小根文件系统需要有:
-
/dev/console
文件、/dev/null
文件当
/etc/inittab
配置文件中没有设置项时会默认到 /dev/null
文件 -
init程序(一般来源于busybox)
/linuxrc、/sbin/init、/etc/init、/bin/init、/bin/sh这些文件最后均会链接到busybox
-
配置文件
/etc/inittab
-
配置文件中指定的应用程序
-
C库
三、配置编译busybox
可以参考busybox/INSTALL中的说明
-
make defconfig采取默认配置项
-
make menuconfig配置
或者直接修改makefile,找到CROSS_COMPILE设置为交叉工具链:arm-linux-
-
make编译
-
make CONFIG_PREFIX=目录:指定编译结果存放的目录
make install 为安装到PC机上
eg:
在/home/null/nfs_root目录下安装编译出来的busybox
make install CONFIG_PREFIX=/home/null/nfs_root
四、构建根文件系统
最精简的根文件系统
-
创建设备文件
/dev/console
、/dev/null
通过观察PC机上相同的设备来创建(在/dev/目录下):
1
2sudo mknod console c 5 1
sudo mknod null c 1 3 -
创建配置文件
/etc/inittab
(最小的配置文件)1
console::askfirst:-/bin/sh
-
安装C库(glibc库)
在开发板上只需要加载器和动态库,假设要构建的根文件系统目录为/ work/nfs_root/fs_mini,操作如下:
1
2
3mkdir -p /work/nfs_root/fs_mini/lib
cd /work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux/lib
cp *.so* /work/nfs_root/fs_mini/lib -d -
通过
mkyaffs2image
工具打包成镜像下载至开发板(需要自己编译,支持NorFlash)1
2mkyaffs2image test_fs/ test_fs.yaffs2
#mkyaffs2image 待打包文件 生成的镜像
完善根文件系统
挂载相关文件系统(2种方法)
-
手动创建proc目录并挂载虚拟文件系统
1
2mkdir proc#创建
mount -t proc none /proc#挂载默认配置文件中会执行
/etc/init.d/rcS
脚本文件,也可以在这里边执行挂载命令mount -t proc none /proc
挂载proc虚拟文件系统,创建后记得chmod +x /etc/init.d/rcS
-
mount -a(该命令也是在/etc/init.d/rcS中)
- 该命令会读出
/etc/fstab
配置文件中的内容来挂载文件系统
etc/fstab
文件被用来定义文件系统的“静态信息”,这些信息被用来控制 mount命令的行为。/etc/fstab
配置文件格式:
device mount-point type options dump fsck order
device :要挂载的设备
mount-point:挂载到哪,挂载点
type:文件系统类型
options:挂接参数,以逗号隔开
dump:
fsck:
order:
例子:
1
2
3#device mount-point type options dump fsck order
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0 - 该命令会读出
2. 可以通过`cat /proc/mounts`命令来查看当前系统中已经挂载的文件系统
mdev
linux操作系统中有udev
,它能够自动在/dev/
下创建设备节点。在嵌入式中,有一个简化版本:mdev
(busybox中自带),使用方法:
-
挂载相关文件系统(这里采用
/etc/fstab
配置文件,直接添加)命令:
1
2mount -t sysfs sysfs /sys
mount -t tmpfs mdev /dev或
/etc/fstab
配置文件中添加:1
2sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0 -
执行相关命令(这里采用
/etc/init.d/rcS
脚本文件,直接添加)1
2
3
4mkdir /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug#热插拔
mdev -s#先将系统中已有的设备节点创建出来
五、NFS服务
服务器设置——PC机
-
安装NFS服务、创建共享文件夹
1
2
3sudo apt-get install nfs-kernel-server
mkdir -p nfs_root
chmod 777 -R nfs_root#修改权限 -
修改NFS配置文件
/etc/exports
(自行修改nfs挂载目录)1
sudo vim /etc/exports
添加如下内容(绝对路径)
1
2
3
4
5
6
7/home/null/NFS_Share *(sync,rw,insecure,no_subtree_check,no_root_squash)
#挂载目录 所有人都可以访问
#rw:读/写权限
#sync:数据同步写入内存和硬盘
#insecure:端口号大于1024时的非法设置
#no_root_squash:服务器允许远程系统以root特权存取该目录
#no_subtree_check:关闭子树检查 -
重启服务
1
2
3
4
5service rpcbind start rpcbi
sudo service nfs-kernel-server restart
或
sudo /etc/init.d/nfs-kernel-server start
sudo /etc/init.d/portmap start -
检查是否配置成功(自己挂接自己)(绝对路径)
1
2
3sudo mount -t nfs -o nolock localhost://home/null/NFS_Share/nfsroot/tmp/fs_mini_mdev /mnt/nfs_test/#挂接
ls -l /mnt/nfs_test#检查
sudo umount /mnt/nfs_test#卸载
客户端设置——开发板
启动linux后手动挂接
-
开发板和服务器能够互ping
-
挂接(绝对路径)
1 | mount -t nfs -o nolock 192.168.1.111:/home/null/NFS_Share/nfsroot/tmp/fs_mini_mdev /mnt |
这里192.168.1.111为服务器ip地址,两者需要在同一网段
-
查看
1
ls /mnt
启动linux后自动挂接
这里是利用了启动后的初始化脚本/etc/init.d/rcS
自动挂载:
修改内容为:
1 | ifconfig eth0 192.168.1.17 |
如果是通过删除脚本再新建的,最后务必修改执行权限:
1 | chmod 777 /etc/init.d/rcS |
启动linux前自动挂接(必须要路由器)
-
uboot中修改启动参数
bootargs
1
set bootargs noinitrd root=/dev/nfs nfsroot=192.168.2.101:/home/null/nfs_root ip=192.168.2.17:192.168.2.101:192.168.2.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0,115200;save
nfsroot=服务器ip:nfs绝对路径 ip=开发板ip:服务器ip:网关:子网掩码::网卡:off
注:原来从flash上启动的参数为
1
2
3set bootargs noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0,115200;save
mount -t nfs -o intr,nolock,rsize=1024,wsize=1024 192.168.2.111:/home/null/NFS_Share/ /mnt &