嵌入式Linux(4):Linux系统构成及启动原理

U-Boot、 Linux kernel 和 rootfs 这三者一起构成了一个完整的 Linux 系统,一个可以正常使用、功能完善的 Linux 系统。
Bootloader 代码用于启动 Linux 内核, Bootloader有很多,常用的就是 U-Boot。移植好 U-Boot 以后再移植 Linux 内核和设备树.dts,移植完 Linux 内核后,还需要再移植一个根文件系统(rootfs),根文件系统里面包含了一些最常用的命令和文件。

U-Boot

Linux 系统要启动就必须需要一个 bootloader 程序,也就说芯片上电以后先运行一段bootloader程序。这段bootloader程序会先初始化DDR等外设,然后将Linux内核从flash(NAND,NOR FLASH, SD, MMC 等)拷贝到 DDR 中,最后启动 Linux 内核。uboot 的全称是 Universal Boot Loader, uboot 是一个遵循 GPL 协议的开源软件, uboot 是一个裸机代码,可以看作是一个裸机综合例程。

U-boot 命令

  1. 信息查询命令
    bdinfo:用于查看板子信息,直接输入“bdinfo”即可
    printenv :用于输出环境变量信息
    version:用于查看 uboot 的版本号

  2. 环境变量操作命令
    setenv: 用于设置或者修改环境变量的值
    saveenv :用于保存修改后的环境变量

  3. 内存操作命令
    用于直接对 DRAM 进行读写操作的,常用的内存操作命令有 md、 nm、mm、 mw、 cp 和 cmp

  4. nfs 命令:将 Ubuntu 中的文件下载到开发板的 DRAM 中

  5. tftp 命令:用于通过网络下载东西到 DRAM 中

  6. uboot 的本质工作是引导 Linux,boot(引导)命令:bootz、 bootm 和 boot

U-Boot 顶层 Makefile 分析

  • 命令输出
    uboot 默认编译是不会在终端中显示完整的命令,都是短命令,设置 V=0 或者在命令行中不定义 V 的话,编译 uboot 的时候终端中显示的短命令,设置V=1完整输出。

  • 静默输出
    编译的时候使用“make -s”即可实现静默输出,不需要输出命令。

  • 设置编译结果输出目录
    uboot 可以将编译出来的目标文件输出到单独的目录中,在 make 的时候使用“O”来指定输出目录,比如“make O=out”就是设置目标文件输出到 out 目录中。这么做是为了将源文件和编译产生的文件分开,当然也可以不指定 O 参数,不指定的话源文件和编译产生的文件都在同一个目录内,一般我们不指定 O 参数。

  • 代码检查
    uboot 支持代码检查,使用命令“make C=1”使能代码检查,检查那些需要重新编译的文件。“make C=2”用于检查所有的源码文件。

  • 模块编译
    在 uboot 中允许单独编译某个模块,使用命令“ make M=dir”即可,旧语法“ makeSUBDIRS=dir”也是支持的

  • 设置目标架构和交叉编译器
    设置目标板架构ARCH 和交叉编译器 CROSS_COMPILE

  • 调用 scripts/Kbuild.include 文件
    主 Makefile 会调用文件 scripts/Kbuild.include 这个文件

  • 交叉编译工具变量设置、导出其他变量

  • 头文件路径变量
    顶层 Makefile 定义了两个变量保存头文件路径: USERINCLUDE 和 LINUXINCLUDE

  • make命令流程
    make命令流程
    make xxx_defconfig: 用于配置 uboot,这个命令最主要的目的就是生成.config 文件。
    make:用于编译 uboot,这个命令的主要工作就是生成二进制的 u-boot.bin 文件和其他的一些与 uboot 有关的文件,比如 u-boot.imx 等等。

  • bootz命令执行过程
    要分析 uboot 的启动流程,首先要找到“入口”,找到第一行程序在哪里。程序的链接是由链接脚本来决定的,所以通过链接脚本可以找到程序的入口。编译一下 uboot,编译完成以后就会在 uboot 根目录下生成 u-boot.lds文件,u-boot.map 是 uboot 的映射文件,可以从此文件看到某个文件或者函数链接到了哪个地址。
    在这里插入图片描述

U-Boot启动流程

uboot运行分两个阶段:运行在SRAM中的汇编阶段、运行在DDR中的C语言阶段
在这里插入图片描述
第一阶段(放在start.S文件中,汇编)

1)_start定义入口(通过链接器脚本来完成)

  • 在链接脚本中可以看到ENTRY()指定的入口,如:ENTRY(_start), _start就是入口(arch/arm/lib/vectors.S)

2)设置异常向量

  • 定义了发生异常的时候跳转到哪个函数去执行,每种异常都应该被处理,防止发生异常跑飞,uboot由于程序简单并未细致处理各种异常,reset就是系统启动时执行的代码,其实每一次系统启动都是一次复位,reset函数( arch/arm/cpu/armv7/start.S )

3)设置CPU速度、时钟频率和中断控制寄存器

  • 设置CPU为svc32模式并关闭FIQ和IRQ。进入底层初始化(lowlevel_init.S)

4)初始化内存控制器(MMU)

5)将ROM中的程序复制到RAM中

6)初始化堆栈

7)转到RAM中执行,该工作可使用指令 (ldr pc)来完成
在这里插入图片描述
第二阶段(C语言部分)

1)调用一系列的初始化函数

2)初始化Flash设备

3)初始化系统内存

4)如果目标机有NAND设备,初始化

5)如果目标机有显示设备,初始化

6)初始化相关网络设备,填写IP,MAC地址等

7)进入命令循环(即整个boot的工作环境),接收用户从串口输入的命令,然后进行相应的工作(main_loop())

在这里插入图片描述

Liunx

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

Linux系统启动流程

系统执行流程可以简单总结为以下几步:
1)开机BIOS自检,加载硬盘。
2)读取MBR,进行MBR引导。
3)grub引导菜单(Boot Loader)。
4)加载内核kernel。
5)启动init进程,依据inittab文件设定运行级别
6)init进程,执行rc.sysinit文件。
7)启动内核模块,执行不同级别的脚本程序。
8)执行/etc/rc.d/rc.local
9)启动mingetty,进入系统登陆界面。

  • 启动第一步--加载BIOS
    当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的重要,以至于计算机必须在最开始就找到它。这是因为BIOS中包含了CPU的相关信息、设备启动顺序信息、硬盘信息、内存信息、时钟信息、PnP特性等等。在此之后,计算机心里就有谱了,知道应该去读取哪个硬件设备了。

  • 启动第二步--读取MBR
    众所周知,硬盘上第0磁道第一个扇区被称为MBR,也就是Master Boot Record,即主引导记录,它的大小是512字节,别看地方不大,可里面却存放了预启动信息、分区表信息。
    系统找到BIOS所指定的硬盘的MBR后,就会将其复制到0×7c00地址所在的物理内存中。其实被复制到物理内存的内容就是Boot Loader,而具体到你的电脑,那就是lilo或者grub了。

  • 启动第三步--Boot Loader
    Boot Loader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核做好一切准备。
    Boot Loader有若干种,其中Grub、Lilo和spfdisk是常见的Loader。我们以Grub为例来讲解吧,毕竟用lilo和spfdisk的人并不多。系统读取内存中的grub配置信息(一般为menu.lst或grub.lst),并依照此配置信息来启动不同的操作系统。

  • 启动第四步--加载内核
    根据grub设定的内核映像所在路径,系统读取内存映像,并进行解压缩操作。此时,屏幕一般会输出“Uncompressing Linux”的提示。当解压缩内核完成后,屏幕输出“OK, booting the kernel”。
    系统将解压后的内核放置在内存之中,并调用start_kernel()函数来启动一系列的初始化函数并初始化各种设备,完成Linux核心环境的建立。至此,Linux内核已经建立起来了,基于Linux的程序应该可以正常运行了。

  • 启动第五步--用户层init依据init.tab文件来设定运行等级
    内核被加载后, 第一个运行的程序便是 /sbin/init,该文件会读取/etc/inittab文件,并依据此文件来进行初始化工作。其实/etc/inittab文件最主要的作用就是设定Linux的运行等级,其设定形式是“:id:5:initdefault:”,这就表明Linux需要运行在等级5上。Linux的运行等级设定如下:

0:关机
1:单用户模式
2:无网络支持的多用户模式
3:有网络支持的多用户模式
4:保留,未使用
5:有网络支持有X-Window支持的多用户模式
6:重新引导系统,即重启

  • 启动第六步--init进程执行rc.sysinit
    在设定了运行等级后,Linux系统执行的第一个用户层文件就是 /etc/rc.d/rc.sysinit脚本程序,它做的工作非常多,包括设定PATH、设定网络配置(/etc/sysconfig/network)、启动swap分区、设定/proc等等。

  • 启动第七步--启动内核模块,执行不同运行级别的脚本程序
    具体是依据 /etc/modules.conf文件或 /etc/modules.d目录下的文件来装载内核模块。根据运行级别的不同,系统会运行rc0.d到rc6.d中的相应的脚本程序,来完成相应的初始化工作和启动相应的服务。

  • 启动第八步--执行/etc/rc.d/rc.local
    rc.local就是在一切初始化工作后,Linux留给用户进行个性化的地方。你可以把你想设置和启动的东西放到这里

  • 启动第九步--执行/bin/login程序,启动mingetty,进入系统登陆界面
    此时,系统已经进入到了等待用户输入username和password的时候了,你已经可以用自己的帐号登入系统了。
    在这里插入图片描述

Linux操作系统的启动过程(第二种)

一般分为四个阶段:

BIOS启动引导阶段;
GRUB启动引导阶段;
内核阶段;
init初始化阶段。

BIOS启动引导阶段
系统上电开机后,主板BIOS运行POST(Power on self test)代码,检测系统外围一些设备(如:CPU、内存、显卡、I/O、键盘鼠标等),当设备检测通过后,系统开始启动自检程序,根据在BIOS中设置的启动顺序搜索启动驱动器(比如硬盘、光驱、网络服务器等),并获取第一个启动设备的代号,读取第一个启动设备的MBR的引导加载程序(即lilo、grub、spfdisk等)的启动信息,从MBR中装载启动引导管理器(GRUB)并运行该启动引导管理。至此进入GRUB启动引导阶段。

GRUB启动引导阶段
主要操作是装载stage1,装载stage1.5,装载stage2。然后读取/boot/grub.conf文件显示启动菜单,装载所选的kernel和initrd文件到内存中。

BIOS通过下面两种方法之一来传递引导记录:

将控制权传递给initial program loader(IPL),该程序安装在磁盘主引导记录(MBR)中。
将控制权传递给initial program loader(IPL),该程序安装在磁盘分区的启动引导扇区中。

上面两种方法比较后,无论上面的哪种情况中,IPL都是MBR的一部分,都需要MBR的参与。

IPL是GRUB阶段的第一个部分(stage1),他的作用就是定位和装载GRUB的第二个部分(stage2);因为stage1没有文件系统识别能力,所以具有文件系统识别能力的stage1.5成为stage1和stage2之间连接的桥梁,这样GRUB才有能力去访问/boot分区/boot/grub目录下的 stage2文件,将stage2载入内存并执行。
stage2对启动系统起关键作用,该部分提供了GRUB启动菜单和交互式的GRUB的shell。

启动菜单在启动时候通过/boot/grub/grub.conf文件所定义的内容生成。

在启动菜单中选择了kernel之后,GRUB会负责解压和装载kernel image并且将initrd装载到内存中,最后GRUB初始化kernel启动代码,完成之后后续的引导权,被移交给kernel。

内核阶段
操作系统的核心是放在文件系统中的,要想正确加载核心就必须提前识别文件系统。

要想加载linux的核心就必须能识别linux的文件系统,核心文件一般会放在/boot/vmlinuz。在系统启动的控制权移交给kernel后,Kernel会立即初始化系统中各设备并做相关配置工作,其中包括CPU、I/O、存储设备等。

配置过程中进行设备驱动加载的时候,一部分设备的驱动编入Linux Kernel中,Kernel会调用这部分驱动初始化相关设备;另外有一部分设备驱动并没有编入Kernel,而是作为模块形式放在initrd中。

initrd是一种基于内存的文件系统,启动过程中,系统在访问真正的根文件系统时,会先访问initrd文件系统。将initrd中的内容打开来看,会发现有bin、dev、etc、lib、proc、sys、sysroot、init等文件(包含目录)。initrd中的内容释放到rootfs(根文件系统)中后,Kernel会执行其中的init文件。这个时候内核的控制权移交给init文件处理。驱动加载后,会创建一个根设备,然后将根文件系统以只读的方式挂载。

这步结束后释放未使用内存并执行switch root,转换到真正的根上面去,同时运行/sbin/init程序,开启系统的1号进程,此后系统启动的控制权移交给 init 进程。

Init初始化阶段
在核心加载完成之后,系统就准备好了,等待程序的执行。整个linux系统中,第一个执行的程序就是“/sbin/init”。

具体步骤如下:
执行系统初始化脚本(/etc/rc.d/rc.sysinit),对系统进行基本的配置,以读写方式挂载根文件系统及其它文件系统,到此系统基本算运行起来了;通过读取配置文件/etc/inittab确定启动后进入的运行级别,在选定运行级别后进入相应的“/etc/rc.d/rcX.d”目录中运行相应的服务内容,该目录下的内容全部都是以S或K开头的链接文件,这些链接文件都链接到“/etc/rc.d/init.d”目录下的各种shell脚本,通过执行这些shell脚本,完成了系统所有的启动任务,linux会启动终端或X-Window来等待用户登录。

Linux系统启动流程图及其相关文件
在这里插入图片描述

根文件系统(rootfs)

Linux 中的根文件系统像是一个文件夹或者叫做目录(在我看来就是一个文件夹,只不过是特殊的文件夹),在这个目录里面会有很多的子目录。根目录下和子目录中会有很多的文件,这些文件是 Linux 运行所必须的,比如库、常用的软件和命令、设备文件、配置文件等等。

根文件系统是 Linux 内核启动以后挂载(mount)的第一个文件系统,然后从根文件系统中读取初始化脚本,比如 rcS, inittab 等。**根文件系统和 Linux 内核是分开的,单独的 Linux 内核是没法正常工作的,必须要搭配根文件系统。**如果不提供根文件系统, Linux 内核在启动的时候就会提示内核崩溃(Kernel panic)的提示

根文件系统结构

  • /bin: binary,二进制文件,可执行程序,shell命令,如: ls , rm , mv, cp等常用命令
  • /dev: device,在linux下一切皆文件 硬盘, 显卡, 显示器、字符设备文件、块设备文件。如: 在input目录下执行: sudo cat mouse0, 移动鼠标会显示有输入。
  • /etc: 存放配置文件
  • /lib: linux运行的时候需要加载的一些动态库,如: libc.so、libpthread.so等
  • /mnt: 临时手动的挂载目录, 如U盘等
  • /proc 虚拟文件系统,数据放置到内存中,存放系统运行信息
  • /usr: usr不是user的缩写,而是UNIX Software Resource的缩写,存放于系统用户有关的文件,会占用很大的存储空间!
  • /var 存放一些可以改变的数据
  • /sbin: s是Super User的意思,这里存放的是系统管理员使用的系统管理程序。sbin下面的一般是系统开机过程中所需要的命令。如ifconfig, halt, shutdown, reboot等系统命令
  • /sys 记录内核信息,虚拟文件系统。
  • /opt: 安装第三方应用程序,比如安装oracle数据库可以在这个目录下

busybox软件集成Linux根文件的命令

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值