root_fs文件系统结构分析和内核加载流程

目录

概述

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给主机额外安装软件所摆放的目录
rootroot 用户的主目录
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 ] ; then
    source /etc/eth0-setting
​
    if grep -q "^/dev/root / nfs " /etc/mtab ; then
        echo -n NFS root ... > /dev/ttySAC0
    else
        ifconfig eth0 down
        ifconfig eth0 hw ether $MAC
        ifconfig eth0 $IP netmask $Mask up
        route add default gw $Gateway
    fi
​
    echo nameserver $DNS > /etc/resolv.conf
else
​
    if grep -q "^/dev/root / nfs " /etc/mtab ; then
        echo -n NFS root ... > /dev/ttySAC0
    else
    /sbin/ifconfig eth0 192.168.1.230 netmask 255.255.255.0 up
    fi
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()的详细代码如下:

  • 52
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值