目录
概述
1 根文件系统下目录介绍
2 文件系统内容分析
2.1 etc/inittab代码分析
2.2 /etc/init.d/rcS 代码分析
2.3 /etc/mdev.conf代码分析
2.3.1 功能概述
2.3.2 /etc/mdev.conf的详细代码
2.4 /etc/init.d/rcS的源代码文件
3 分析内核中加载root_fs的流程
3.1 调用入口一:vfs_caches_init
3.1.1 init_rootfs
3.1.2 init_mount_tree
3.1.2.1 vfs_kern_mount
3.1.2.2 配置root path
3.2 调用入口二: rest_init函数
3.2.1 函数调用关系
3.2.2 kernel_init函数
概述
本文主要详细分析了root_fs文件系统结构,对该文件目录下的每个类型的文件做了详细的介绍,并讲述各个文件的作用,其在内核中主要实现了什么功能,还介绍了内核加载root_fs的过程,详细跟踪了整个的加载流程,介绍每一个函数的调用关系。
1 根文件系统下目录介绍
嵌入式 Linux 中都需要构建根文件系统,构建根文件系统的规则在 FHS(Filesystem Hierarchy Standard)文档中,下面是根文件系统顶层目录。
目录名称 | 内容 |
---|---|
bin | 所有用户都可以使用的、基本的命令。 |
sbin | 基本的系统命令,它们用于启动系统、修复系统等。 |
usr | 共享、只读的程序和数据。 |
proc | 这是个空目录,常作为 proc 文件系统的挂载点。 |
dev | 该目录存放设备文件和其它特殊文件。 |
etc | 系统配置文件,包括启动文件。 |
lib | 存放共享库和可加载块(即驱动程序),共享库用于启动系统、运行根文件系统中的可执行程序。 |
boot | 引导加载程序使用的静态文件 |
home | 用户主目录,包括供服务账号锁使用的主目录,如 FTP |
mnt | 临时挂接某个文件系统的挂接点,通常是空目录。也可以在里面创建空的子目 录。 |
opt | 给主机额外安装软件所摆放的目录 |
root | root 用户的主目录 |
tmp | 存放临时文件,通常是空目录。 |
var | 存放可变的数据。 |
2 文件系统内容分析
默认的内核命令行上有 init=/linuxrc, 因此,在文件系统被挂载后,运行的第一个程序是根目录下的 linuxrc。 这是一个指向/bin/busybox 的链接,也就是说,系统起来后运行的第一个程序也就是 busybox 本身。
busybox首先将试图解析/etc/inittab 来获取进一步的初始化配置信息(参考 busybox 源代码 init/init.c 中)parse_inittab()函数。而事实上, root_qtopia 中并没有/etc/inittab 这个配置文件,根据 busybox 的逻辑,它将生成默认的配置 。
2.1 etc/inittab代码分析
查看busybox-1.13.3/init/init.c文件中parse_inittab()函数的源码:
代码第701行: new_init_action(SYSINIT, INIT_SCRIPT, ""),也就决定了接下去初始化的脚本是 INIT_SCRIPT 所定义的值。这个宏的默认值是"/etc/init.d/rcS".
#define INITTAB "/etc/inittab" /* inittab file location */
#ifndef INIT_SCRIPT
#define INIT_SCRIPT "/etc/init.d/rcS" /* Default sysinit script. */
#endif
parse_inittab() 函数的源代码
2.2 /etc/init.d/rcS 代码分析
该文件的路径: /etc/init.d/rcS。详细分析代码如下:
代码第3~5行: 为启动环境设置必要的环境变量
代码第13行: 设置机器的名称
代码第15~18行:
挂载“虚拟”文件系统“/proc”和“/sys”,并且在/dev 目录上挂载一个 ramfs,相当于把原本 NAND Flash 上的只读的/dev 目录“覆盖”上一块可写的空的 SDRAM。
/sys 和挂载了 ramfs 的/dev 是正确创建设备节点的关键。对于 2.6.29 内核来说,已经没有了 devfs 的支持,创建设备节点只有通过两种办法由文件系统完成:
1) 制作文件系统镜像前用 mknod 手动创建好系统中所有的(包括可能有的)设备节点, 并把这些节点文件一起做进文件系统镜像中;
2)在文件系统初始化过程中,通过/sys 目录所输出的信息,在/dev 目录下动态的创建 系统中当前实际有的设备节点
代码第20~22行:
1)通过 mdev -s 在/dev 目录下建立必要的设备节点;
2)设置内核的 hotplug handler 为 mdev, 即当设备热插拔时,由 mdev 接收 来自内核的消息并作出相应的回应, 比如挂载 U 盘。
对于 mdev,需要注意的是,文件系统里存在/etc/mdev.conf 文件,它包含了 mdev 的配置信息。
代码第24~35行:用来挂载其他一些常用的文件系统,并在/var 目录下(同样是ramfs,可写的)新建必要的目录。
代码第36行:用来设定系统时间的,从硬件 RTC 中获取,要获取正确的时间,必须先设置好正确的时间(如何设置 RTC 见用户手册说明)。
代码第39~54行:启动一系列服务:
服务类型 | 说明 |
---|---|
syslog | 用于记录内核和应用程序 debug 信息 |
netd - inetd | 一个挂载启动各种网络相关服务的看守进程 |
httpd - http server | 进程 |
leds | 跑马灯看守进程 |
代码47~48行:配置网络设备(网卡): 设定本机回环地址为 127.0.0.1 2)运行网卡设置脚本/etc/init.d/ifconfig-eth0
查看/etc/init.d/ifconfig-eth0 文件的内容:
#!/bin/sh
echo -n Try to bring eth0 interface up......>/dev/ttySAC0
if [ -f /etc/eth0-setting ] ; thensource /etc/eth0-setting
if grep -q "^/dev/root / nfs " /etc/mtab ; thenecho -n NFS root ... > /dev/ttySAC0elseifconfig eth0 downifconfig eth0 hw ether $MACifconfig eth0 $IP netmask $Mask uproute add default gw $Gatewayfi
echo nameserver $DNS > /etc/resolv.conf
else
if grep -q "^/dev/root / nfs " /etc/mtab ; thenecho -n NFS root ... > /dev/ttySAC0else/sbin/ifconfig eth0 192.168.1.230 netmask 255.255.255.0 upfi
fi
echo Done > /dev/ttySAC0
查看/etc/eth0-setting,源码如下:
2.3 /etc/mdev.conf代码分析
2.3.1 功能概述
在 /etc/mdev.conf文件查看源码:
1)原本串口驱动注册的设备名是 s3c2410_serial0, s3c2410_serial1 和s3c2410_serial2,而 mdev 则会在/dev 目录下对应生成 ttySAC0, ttySAC1 和 ttySAC2 以符合应用程序对于串口设备名称的习惯。
2) /dev/sdcard 和/dev/udisk 永远分别指向 SD 卡和U 盘的第一个分区。
3) 当 SD 卡或者 U 盘插入/拔出时,将这个消息传递给自定义的热插拔 handler, /bin/hotplug. 这个程序用于自动挂载可移动设备的,目前是 SD卡和 U 盘。它的逻辑很简单,将 SD 卡或者 U 盘的第一个分区作为 FAT/FAT32 挂载到/sdcard或者/udisk.
注意的问题:
当 SD 卡或者 U 盘上没有分区表或者第一个分区不是 FAT/FAT32 格式的时候,它就不能正常工作
2.3.2 /etc/mdev.conf的详细代码
# system all-writable devices
full 0:0 0666
null 0:0 0666
ptmx 0:0 0666
random 0:0 0666
tty 0:0 0666
zero 0:0 0666
# console devices
tty[0-9]* 0:5 0660
vc/[0-9]* 0:5 0660
# serial port devices
s3c2410_serial0 0:5 0666 =ttySAC0
s3c2410_serial1 0:5 0666 =ttySAC1
s3c2410_serial2 0:5 0666 =ttySAC2
s3c2410_serial3 0:5 0666 =ttySAC3
# loop devices
loop[0-9]* 0:0 0660 =loop/
# i2c devices
i2c-0 0:0 0666 =i2c/0
i2c-1 0:0 0666 =i2c/1
# frame buffer devices
fb[0-9] 0:0 0666
# input devices
mice 0:0 0660 =input/
mouse.* 0:0 0660 =input/
event.* 0:0 0660 =input/
ts.* 0:0 0660 =input/
# rtc devices
rtc0 0:0 0644 >rtc
rtc[1-9] 0:0 0644
# misc devices
mmcblk0p1 0:0 0600 =sdcard */bin/hotplug.sh
sda1 0:0 0600 =udisk * /bin/hotplug.sh
2.4 /etc/init.d/rcS的源代码文件
#! /bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin:
runlevel=S
prevlevel=N
umask 022
export PATH runlevel prevlevel
#
# Trap CTRL-C &c only in this shell so we can interrupt subprocesses.
#
trap ":" INT QUIT TSTP
/bin/hostname FriendlyARM
/bin/mount -n -t proc none /proc
/bin/mount -n -t sysfs none /sys
/bin/mount -n -t usbfs none /proc/bus/usb
/bin/mount -t ramfs none /dev
echo /sbin/mdev > /proc/sys/kernel/hotplug
/sbin/mdev -s
/bin/hotplug
# mounting file system specified in /etc/fstab
mkdir -p /dev/pts
mkdir -p /dev/shm
/bin/mount -n -t devpts none /dev/pts -o mode=0622
/bin/mount -n -t tmpfs tmpfs /dev/shm
/bin/mount -n -t ramfs none /tmp
/bin/mount -n -t ramfs none /var
mkdir -p /var/empty
mkdir -p /var/log
mkdir -p /var/lock
mkdir -p /var/run
mkdir -p /var/tmp
/sbin/hwclock -s
syslogd
/etc/rc.d/init.d/netd start
echo " " > /dev/tty1
echo "Starting networking..." > /dev/tty1
/etc/rc.d/init.d/leds start
echo " " > /dev/tty1
echo "Starting leds service..." > /dev/tty1
echo " "
/sbin/ifconfig lo 127.0.0.1
/etc/init.d/ifconfig-eth0
soundplayer /root/testsound.mp3&
/bin/rtm&
echo " " > /dev/tty1
echo "Starting MiniTest, please waiting..." > /dev/tty1
3 分析内核中加载root_fs的流程
内核的启动时从start_kernel()开始的,在该函数中调用vfs_caches_init() 和rest_init函数。其具体函数调用关系如下图所示:
3.1 调用入口一:vfs_caches_init
文件地址: \init\main.c
vfs_caches_init()所在的文件地址: \fs\dcache.c
在vfs_caches_init()函数中调用mnt_init()函数
mnt_init()函数所在文件地址:\fs\namespace.c
在mnt_init()函数中调用 init_rootfs()和init_mount_tree()初始化和挂载文件系统
函数调用关系如下:
3.1.1 init_rootfs
该函数所在的文件地址:fs\namespace.c
查看init_rootfs()的源码:
在init_rootfs函数中初始化RAM fs,函数调用关系:
3.1.2 init_mount_tree
在函数中匹配root_fs的类型;init_mount_tree()所在的文件地址:\fs\namespace.c
函数调用关系:
3.1.2.1 vfs_kern_mount
init_mount_tree()函数调用vfs_kern_mount()函数挂载文件系统
在vfs_kern_mount()函数中调用mount_fs(),函数中的调用关系
3.1.2.2 配置root path
在init_mount_tree()中还调用两个重要的函数:
代码3090行: set_fs_pwd()
代码3091行: set_fs_root()
3.2 调用入口二: rest_init函数
3.2.1 函数调用关系
函数调用关系:
函数rest_init中的细节如下:
代码394行: 该线程调用kernel_init,初始化root_fs其他的参数
3.2.2 kernel_init函数
函数调用关系:
kernel_init()的详细代码如下: