1.ARM芯片上电启动流程
我们首先来了解几个关键词:
IROM (Internal ROM):芯片内部固化存储代码的存储器
IRAM (Internal RAM):在IROM启动运行的时候,外置SDRAM还没有初始化好,而IRAM是可用的,因此必须要把BL1加载到IRAM中运行,由BL1对SDRAM进行初始化。
Nand Flash :芯片外部非易失闪存技术的存储器,没有地址总线,不能直接运行代码,需要将代码加载到RAM上面才能运行。
SDRAM:芯片外部的运行内存,由运行在IRAM上的BL1进行初始化,用于运行BL2与应用程序。
BL0:固化在芯片iROM中的启动代码。
作用:初始化PPL和时钟,设置看门狗,关闭中断和看门狗,初始化堆和栈;判断硬件的启动方式,去不同的启动方式的启动设备里面加载BL1。
BL1:是指在IRAM自动从外扩存储器(nand/sd/usb)中拷贝的uboot.bin二进制文件的头,最大16K(一般是4K或8K)代码。
作用:初始化硬件,初始化RAM,初始化串口,分配堆栈空间,清空.BSS段,加载BL2。
BL2:是指在代码重定向后在内存中执行的uboot的完整代码。
作用:初始化本阶段要使用的硬件设备,加载内核,将内核映像和根文件系统映像从FLASH上读到RAM空间中,为内核设置启动参数。
上电启动流程:
- CPU先读取运行IROM里面固化的启动代码BL0,进行一些基本的初始化,比如初始化PLL和时钟,关闭看门狗初始化堆栈,将flash中BL1的代码加载到IRAM中,跳转到IRAM中BL1的执行地址执行BL1。
- 在BL1中初始化硬件设备,初始化串口,初始化SDRAM,将flash中BL2的代码加载到SDRAM中,分配堆栈空间,设置好栈跳转到SDRAM中BL2的执行地址执行BL2。
- 初始化本阶段使用的硬件设备,初始化MMU,检测系统内存映射, 将内核和文件系统从Flash读取到RAM中,为内核设置启动参数,加载内核,挂载文件系统。
note:
- 1、为什么BL0不初始化SDRAM呢?
那是因为支持的SDRAM规格是可变的,由固化代码来初始化显得不够灵活,而且固化代码往往代码量比较小,因为越多越容易出BUG。- 2、为什么不把BL2也加载到IRAM上运行?
因为IRAM非常小,一般是96KB,而BL2一般会比96KB大,所以要在BL1中初始化SDRAM,然后将BL2加载到SDRAM中运行。
1.1 内核引导
当计算机打开电源后,首先是 BIOS开机自检,按照BIOS中设置的启动设备(通常是硬盘)来启动。操作系统接管硬件以后,首先读入 /boot 目录下的内核文件。
1.2 运行init初始化进程—— 初始化系统环境
init 进程是系统所有进程的起点,你可以把它比拟成系统所有进程的老祖宗,没有这个进程,系统中任何进程都不会启动。
由于init是 第一个运行的程序,它的进程编号(pid)就是1。其他所有进程都从它衍生,都是它的子进程。
注意,Debian Linux环境下,内核文件加载以后,就开始运行第一个程序 /sbin/init 。
1.3 运行级别 —— runlevel
许多程序需要开机启动。在 Windows叫做" 服务“(service),在 Linux就叫做” 守护进程“(daemon)。
init进程的一大任务,就是去运行这些开机启动的程序。
但是,不同的场合需要启动不同的程序,比如用作服务器时,需要启动Apache,用作桌面就不需要。 Linux允许为不同的场合,分配不同的开机启动程序,这就叫做"运行级别"(runlevel)。也就是说,启动时根据"运行级别”,确定要运行哪些程序。
Linux预置七种运行级别(0-6)。一般来说,0是关机,1是单用户模式(也就是维护模式),6是重启。运行级别2-5,各个发行版不太一样,对于Debian来说,都是同样的多用户模式(也就是正常模式)。
运行级别0:系统停机状态,系统默认运行级别不能设为0,否则不能正常启动
运行级别1:单用户工作状态,root权限,用于系统维护,禁止远程登录
运行级别2:多用户状态(没有NFS)
运行级别3:完全的多用户状态(有NFS),登录后进入控制台命令行模式
运行级别4:系统未使用,保留
运行级别5:X11控制台,登录后进入图形GUI模式
运行级别6:系统正常关闭并重启,默认运行级别不能设为6,否则不能正常启动
那么,运行级别有些什么程序呢,系统怎么知道每个级别应该加载哪些程序呢?每个运行级别在/etc目录下面,都有一个对应的子目录,指定要加载的程序。
/etc/rc0.d
/etc/rc1.d
/etc/rc2.d
/etc/rc3.d
/etc/rc4.d
/etc/rc5.d
/etc/rc6.d
上面目录名中的" rc",表示 run command(运行程序),最后的d表示 directory(目录)。下面让我们看看树莓派 /etc/rc3.d 目录中到底指定了哪些程序。
pi@raspberrypi:/etc $ ls -al /etc/rc3.d
total 16
drwxr-xr-x 2 root root 4096 Nov 9 15:03 .
drwxr-xr-x 134 root root 12288 Nov 18 16:34 ..
lrwxrwxrwx 1 root root 13 Sep 22 01:13 K01fio -> ../init.d/fio
lrwxrwxrwx 1 root root 22 Sep 22 01:07 S01avahi-daemon -> ../init.d/avahi-daemon
lrwxrwxrwx 1 root root 19 Sep 22 01:06 S01bluetooth -> ../init.d/bluetooth
lrwxrwxrwx 1 root root 26 Sep 22 01:07 S01console-setup.sh -> ../init.d/console-setup.sh
lrwxrwxrwx 1 root root 14 Sep 22 01:03 S01cron -> ../init.d/cron
lrwxrwxrwx 1 root root 14 Sep 22 01:13 S01cups -> ../init.d/cups
lrwxrwxrwx 1 root root 22 Sep 22 01:13 S01cups-browsed -> ../init.d/cups-browsed
lrwxrwxrwx 1 root root 14 Sep 22 01:06 S01dbus -> ../init.d/dbus
lrwxrwxrwx 1 root root 16 Sep 22 01:07 S01dhcpcd -> ../init.d/dhcpcd
lrwxrwxrwx 1 root root 16 Nov 9 15:03 S01docker -> ../init.d/docker
lrwxrwxrwx 1 root root 24 Sep 22 01:07 S01dphys-swapfile -> ../init.d/dphys-swapfile
lrwxrwxrwx 1 root root 17 Sep 22 01:09 S01lightdm -> ../init.d/lightdm
lrwxrwxrwx 1 root root 17 Sep 22 01:03 S01paxctld -> ../init.d/paxctld
lrwxrwxrwx 1 root root 18 Sep 22 01:10 S01plymouth -> ../init.d/plymouth
lrwxrwxrwx 1 root root 37 Sep 22 01:13 S01pulseaudio-enable-autospawn -> ../init.d/pulseaudio-enable-autospawn
lrwxrwxrwx 1 root root 22 Sep 22 01:05 S01raspi-config -> ../init.d/raspi-config
lrwxrwxrwx 1 root root 26 Sep 22 01:06 S01rng-tools-debian -> ../init.d/rng-tools-debian
lrwxrwxrwx 1 root root 15 Sep 22 01:06 S01rsync -> ../init.d/rsync
lrwxrwxrwx 1 root root 17 Sep 22 01:03 S01rsyslog -> ../init.d/rsyslog
lrwxrwxrwx 1 root root 15 Sep 22 01:13 S01saned -> ../init.d/saned
lrwxrwxrwx 1 root root 13 Sep 22 01:07 S01ssh -> ../init.d/ssh
lrwxrwxrwx 1 root root 14 Sep 22 01:06 S01sudo -> ../init.d/sudo
lrwxrwxrwx 1 root root 22 Sep 22 01:05 S01triggerhappy -> ../init.d/triggerhappy
pi@raspberrypi:/etc $
可以看到:文件名都是" 字母S/K+两位数字+程序名"的形式
字母S表示Start,也就是启动的意思(启动脚本的运行参数为start)
字母K表示Kill,也就是关闭的意思,即如果从其他运行级别切换过来,需要关闭的程序(启动脚本的运行参数为stop)。保证是当init改变运行级别时,所有相关的守护进程都将重启。
后面的两位数字表示处理顺序,数字越小越早处理。数字相同时,则按照程序名的字母顺序启动,所以avahi-daemon会先于bluetooth启动。
这个目录里的所有文件就是启动时要加载的程序。如果想增加或删除某些程序,不建议手动修改 /etc/rcN.d 目录,最好是用一些专门命令进行管理。
1.4 系统初始化 —— 加载开机启动程序
前面提到,七种预设的"运行级别"各自有一个目录,存放需要开机启动的程序。不难想到,如果多个"运行级别"需要启动同一个程序,那么这个程序的启动脚本,就会在每一个目录里都有一个拷贝。这样会造成管理上的困扰:如果要修改启动脚本,岂不是每个目录都要改一遍?
Linux的解决办法,就是七个 /etc/rcN.d 目录里列出的程序,都设为 链接文件,指向另外一个目录 /etc/init.d ,真正的启动脚本都统一放在这个目录中。init进程逐一加载开机启动程序,其实就是运行这个目录里的启动脚本。
这样做的另一个好处,就是如果你要手动关闭或重启某个进程,直接到目录 /etc/init.d 中寻找启动脚本即可。比如,我要重启ssh,就运行下面的命令:
sudo /etc/init.d/ssh restart
/etc/init.d 这个目录名最后一个字母d,是directory的意思,表示这是一个目录,用来与程序 /etc/init 区分。
1.5 用户登录
开机启动程序加载完毕以后,就要让用户登录了
一般来说,用户的登录方式有三种:
- 命令行登录
- ssh登录
- 图形界面登录
这三种情况,都有自己的方式对用户进行认证。
1.5.1 方式1:命令行登录
init进程调用getty程序(意为get teletype),让用户输入用户名和密码。输入完成后,再调用login程序,核对密码(Debian还会再多运行一个身份核对程序/etc/pam.d/login)。如果密码正确,就从文件 /etc/passwd 读取该用户指定的shell,然后启动这个shell。
1.5.2 方式2:ssh登录(常用)
这时系统调用sshd程序(Debian还会再运行/etc/pam.d/ssh ),取代getty和login,然后启动shell。
1.5.3 方式3:图形界面登录(常用)
init进程调用显示管理器,Gnome图形界面对应的显示管理器为gdm(GNOME Display Manager),然后用户输入用户名和密码。如果密码正确,就读取/etc/gdm3/Xsession,启动用户的会话。
1.6 进入 login shell
所谓shell,简单说就是命令行界面,让用户可以直接与操作系统对话。用户登录时打开的shell,就叫做login shell。
Debian默认的shell是Bash,它会读入一系列的配置文件。上一步的三种情况,在这一步的处理,也存在差异。
(1)命令行登录:首先读入 /etc/profile,这是对所有用户都有效的配置;然后依次寻找下面三个文件,这是针对当前用户的配置。
~/.bash_profile
~/.bash_login
~/.profile
需要注意的是,这三个文件只要有一个存在,就不再读入后面的文件了。比如,要是 ~/.bash_profile 存在,就不会再读入后面两个文件了。
(2)ssh登录:与第一种情况完全相同。
(3)图形界面登录:只加载 /etc/profile 和 /.profile。也就是说,/.bash_profile 不管有没有,都不会运行。
1.7 打开 non-login shell
老实说,上一步完成以后,Linux的启动过程就算结束了,用户已经可以看到命令行提示符或者图形界面了。但是,为了内容的完整,必须再介绍一下这一步。
用户进入操作系统以后,常常会再手动开启一个shell。这个shell就叫做 non-login shell,意思是它不同于登录时出现的那个shell,不读取/etc/profile和.profile等配置文件。
non-login shell的重要性,不仅在于它是用户最常接触的那个shell,还在于它会读入用户自己的bash配置文件 ~/.bashrc。大多数时候,我们对于bash的定制,都是写在这个文件里面的。
你也许会问,要是不进入 non-login shell,岂不是.bashrc就不会运行了,因此bash 也就不能完成定制了?事实上,Debian已经考虑到这个问题了,请打开文件 ~/.profile,可以看到下面的代码:
if [ -n "$BASH_VERSION" ]; then if [ -f "$HOME/.bashrc" ]; then . "$HOME/.bashrc" fi fi
上面代码先判断变量 $BASH_VERSION 是否有值,然后判断主目录下是否存在.bashrc 文件,如果存在就运行该文件。第三行开头的那个点,是source命令的简写形式,表示运行某个文件,写成"source ~/.bashrc"也是可以的。
Bash的设置之所以如此繁琐,是由于历史原因造成的。早期的时候,计算机运行速度很慢,载入配置文件需要很长时间,Bash的作者只好把配置文件分成了几个部分,阶段性载入。系统的通用设置放在 /etc/profile,用户个人的、需要被所有子进程继承的设置放在.profile,不需要被继承的设置放在.bashrc。
顺便提一下,除了Linux以外, Mac OS X 使用的shell也是Bash。但是,它只加载.bash_profile,然后在.bash_profile里面调用.bashrc。而且,不管是ssh登录,还是在图形界面里启动shell窗口,都是如此。
2.树莓派启动流程
加电后,CPU可以在GPU初始化完成后再进行初始化工作。
BCM283x芯片包含了一个处理视频内核的GPU以及一个ARM CPU 内核。这个GPU包含了一个DSP处理器以及用于图像,视频编解码,3D图形和图像合成的加速器。在BCM283x设备中,GPU的DSP核是最先启动的。DSP主要负责在启动ARM处理器之前通用的设置和整理。
树莓派和CM板在启动阶段分为4个阶段:
- 1.当给树莓派加电后,最先执行保存在ROM中的代码,这些代码是芯片出厂的时候就设定的,通常被称为 bootloader ,这些代码固化硬件内部,可以认为是SoC硬件的一部分,用户无法修改。其作用是挂载SD卡上的FAT32分区,从而加载下一阶段的bootloader。
- 2.需要说明的是,上电或者重启后,cpu和ram都没有初始化,因此,执行的实体是GPU。GPU的DSP复位并执行芯片内部ROM中一小段代码。这段代码的唯一目的就是通过一个外部的接口启动加载第二阶段的引导加载程序。在树莓派和CM板上,这段代码首先在SD卡或者emmc上寻找第二阶段启动加载程序,这个程序文件名是 bootcode.bin ,这个文件位于一个FAT32文件系统的第一分区。如果在SD卡或者emmc中没有发现 bootcode.bin 文件,那么启动ROM代码就会处于USB启动模式,期望通过USB接口获得第二阶段的启动加载程序。
第二阶段启动加载程序(SD卡或emmc启动是bootcode.bin,USB启动是usbbootcode.bin)主要负责设置LPDDR2 SDRAM 接口(初始化ram)和多种其他关键的系统功能,然后加载和执行GPU固件start.elf (在SD卡的初始分区也就是boot分区中)到内存中,随后启动GPU。- 3.GPU启动后会检索配置文件(config.txt、fixup.dat),根据其内容设置CPU运行参数及内存分配情况,随后将用户代码加载至内存,启动CPU。文件start.elf 接管和负责进一步的系统设置以及启动ARM处理器子系统,包含了运行GPU各个部分的固件。start.elf文件首先读取dt-blob.bin (有些会编译进去start.elf文件)文件以便初始化gpio引脚的状态以及GPU特定的接口和时钟,然后再解析文件config.txt 。以上这些完成后,在开始arm子系统以及将设备树的数据传递给Linux内核之前,这个文件把ARM设备树文件 (如:3B板bcm2710-rpi-3-b.dtb )、kernel8.img 加载到内存的预定地址,然后向CPU发出重启信号,因此CPU就可以从内存的预定地址执行kernel的代码,就进入了软件定义的系统启动流程。
- 4.通常情况下,CPU启动后便开始执行kernel8.img中的指令,初始化操作系统内核,在某些情况下,也可以被u-boot代替,又u-boot来加载内核。在树莓派1代中,此阶段部分被保存在kernel.img文件中,2代中,该文件更名为kernel7.img,3代中,该文件更名为kernel8.img。
2.1 树莓派 boot分区
树莓派 /boot 放置Linux内核以及其他用来启动树莓派的软件包。
树莓派引导文件存储在SD卡的第一个分区中,即/boot分区,该分区的文件系统是FAT,所以可以在Windows,macOS和Linux设备上读取这个分区。
pi@raspberrypi:/boot $ ls -al
total 49892
drwxr-xr-x 4 root root 4096 Jan 1 1970 .
drwxr-xr-x 18 root root 4096 Nov 11 05:33 ..
-rwxr-xr-x 1 root root 28111 Nov 8 14:49 bcm2708-rpi-b.dtb
-rwxr-xr-x 1 root root 28418 Nov 8 14:49 bcm2708-rpi-b-plus.dtb
-rwxr-xr-x 1 root root 27790 Nov 8 14:49 bcm2708-rpi-b-rev1.dtb
-rwxr-xr-x 1 root root 28032 Nov 8 14:49 bcm2708-rpi-cm.dtb
-rwxr-xr-x 1 root root 27856 Nov 8 14:49 bcm2708-rpi-zero.dtb
-rwxr-xr-x 1 root root 29267 Nov 8 14:49 bcm2708-rpi-zero-w.dtb
-rwxr-xr-x 1 root root 29305 Nov 8 14:49 bcm2709-rpi-2-b.dtb
-rwxr-xr-x 1 root root 29304 Nov 8 14:49 bcm2709-rpi-cm2.dtb
-rwxr-xr-x 1 root root 30170 Nov 8 14:49 bcm2710-rpi-2-b.dtb
-rwxr-xr-x 1 root root 31922 Nov 8 14:49 bcm2710-rpi-3-b.dtb
-rwxr-xr-x 1 root root 32533 Nov 8 14:49 bcm2710-rpi-3-b-plus.dtb
-rwxr-xr-x 1 root root 30157 Nov 8 14:49 bcm2710-rpi-cm3.dtb
-rwxr-xr-x 1 root root 31230 Nov 8 14:49 bcm2710-rpi-zero-2.dtb
-rwxr-xr-x 1 root root 31230 Nov 8 14:49 bcm2710-rpi-zero-2-w.dtb
-rwxr-xr-x 1 root root 52457 Nov 8 14:49 bcm2711-rpi-400.dtb
-rwxr-xr-x 1 root root 52325 Nov 8 14:49 bcm2711-rpi-4-b.dtb
-rwxr-xr-x 1 root root 52910 Nov 8 14:49 bcm2711-rpi-cm4.dtb
-rwxr-xr-x 1 root root 50224 Nov 8 14:49 bcm2711-rpi-cm4s.dtb
-rwxr-xr-x 1 root root 52476 Nov 8 14:49 bootcode.bin
-rwxr-xr-x 1 root root 147 Sep 22 02:01 cmdline.txt
-rwxr-xr-x 1 root root 2075 Sep 22 01:04 config.txt
-rwxr-xr-x 1 root root 18693 Nov 8 14:49 COPYING.linux
-rwxr-xr-x 1 root root 3170 Nov 8 14:49 fixup4cd.dat
-rwxr-xr-x 1 root root 5400 Nov 8 14:49 fixup4.dat
-rwxr-xr-x 1 root root 8382 Nov 8 14:49 fixup4db.dat
-rwxr-xr-x 1 root root 8386 Nov 8 14:49 fixup4x.dat
-rwxr-xr-x 1 root root 3170 Nov 8 14:49 fixup_cd.dat
-rwxr-xr-x 1 root root 7265 Nov 8 14:49 fixup.dat
-rwxr-xr-x 1 root root 10229 Nov 8 14:49 fixup_db.dat
-rwxr-xr-x 1 root root 10227 Nov 8 14:49 fixup_x.dat
-rwxr-xr-x 1 root root 145 Sep 22 02:01 issue.txt
-rwxr-xr-x 1 root root 6631856 Nov 8 14:48 kernel7.img
-rwxr-xr-x 1 root root 7040088 Nov 8 14:48 kernel7l.img
-rwxr-xr-x 1 root root 8193871 Nov 8 14:49 kernel8.img
-rwxr-xr-x 1 root root 6269568 Nov 8 14:48 kernel.img
-rwxr-xr-x 1 root root 1594 Nov 8 14:49 LICENCE.broadcom
drwxr-xr-x 2 root root 24576 Nov 8 14:49 overlays
-rwxr-xr-x 1 root root 805084 Nov 8 14:49 start4cd.elf
-rwxr-xr-x 1 root root 3746856 Nov 8 14:49 start4db.elf
-rwxr-xr-x 1 root root 2250656 Nov 8 14:49 start4.elf
-rwxr-xr-x 1 root root 2998120 Nov 8 14:49 start4x.elf
-rwxr-xr-x 1 root root 805084 Nov 8 14:49 start_cd.elf
-rwxr-xr-x 1 root root 4818728 Nov 8 14:49 start_db.elf
-rwxr-xr-x 1 root root 2974880 Nov 8 14:49 start.elf
-rwxr-xr-x 1 root root 3721800 Nov 8 14:49 start_x.elf
drwxr-xr-x 2 root root 2048 Nov 4 22:30 'System Volume Information'
pi@raspberrypi:/boot $
当树莓派通电时,它将从启动分区(/boot)中加载各种文件以启动各种处理器,然后启动Linux内核。当Linux启动后,启动分区将挂载为/boot。
文件名 | 说明 |
---|---|
COPYING.linux | 树莓派系统中一个记录Linux内核版权信息的文件 |
LICENCE.broadcom | 是Broadcom公司提供的,通常用于支持Broadcom芯片(如无线网卡、 display/图形驱动程序)的使用。这个文件包含了许可证信息,用于确保硬件的合法使用 |
issue.txt | 记录在启动过程中可能出现的问题和警告 |
‘System Volume Information’ | 这是一个 Windows 特定的隐藏系统目录,通常会出现在 FAT 文件系统中 |
overlays | 存放设备树, 必须有,通过编译Linux内核生成的 |
***.dtb | 设备树二进制文件,描述 RPI板上的硬件组件信息,如处理器、外设等。不同的文件对应于不同型号的 RPI |
bootcode.bin | 第二阶段的bootloader,必须存在(4B除外),负责初始化硬件并加载 start*.elf 文件 |
start*.elf | GPU基本固件,第三阶段启动器,负责加载内核和启动树 |
fixup*.dat | 链接器文件,与 start*.elf 文件匹配 ,这些是固件文件,包含了一些用于修复和初始化硬件的固件信息 |
config.txt | 该文件是启动过程第三阶段用来读取参数的,用于配置系统启动时的参数,如分辨率、内存分配等 |
cmdline.txt | 是树莓派原生系统启动时传给内核的参数,例如根文件系统的位置、启动选项等 |
kernel.img | linux内核镜像,也可以为uboot,然后再进一步启动内核 |
2.1.1 COPYING.linux
COPYING.linux 文件是树莓派系统中一个记录Linux内核版权信息的文件。它包含了树莓派使用的Linux内核的许可证和版权信息。通常,当你看到boot目录下有这个文件时,意味着你可能正在查看一个包含了Linux内核的系统分区。
这个文件本身并不需要执行任何操作,它只是提供了使用的Linux内核的版权信息。
2.1.2 LICENCE.broadcom
LICENCE.broadcom 文件是树莓派中与Broadcom的无线驱动程序相关的许可证文件。通常,当您使用带有Wi-Fi或蓝牙功能的树莓派时,这个文件是必须的,因为它包含了使用Broadcom无线芯片所需的许可信息。
如果在树莓派的boot目录下看到这个文件缺失或损坏,可能会遇到连接Wi-Fi时遇到问题。如果不需要Wi-Fi功能,也可以选择移除brcm的firmware包以减少系统负载,命令如下:
sudo apt-get remove firmware-brcm80211
2.1.3 issue.txt
issue.txt 文件位于树莓派的boot分区,其中记录了在启动过程中可能出现的问题和警告。这个文件对于初次使用树莓派的用户来说可能很重要,因为它可以帮助解决启动时遇到的问题。
如果在启动树莓派后,issue.txt 文件中有关于硬件问题的警告或错误信息,可以根据提示进行硬件检查或更新固件。例如,如果文件中提到了显示问题,可能需要检查显示器或连接线;如果提到了SD卡读写问题,可能需要检查SD卡是否已满或损坏,或尝试重新格式化SD卡。
2.1.4 ‘System Volume Information’
‘System Volume Information’ 文件夹是Windows系统中用于存储当前系统卷的各种备份和恢复点信息的地方,这些信息可以帮助恢复系统到某个特定的状态。
在树莓派的boot分区下通常不会有这个文件夹,因为这是Linux文件系统的特性,如果不需要这个文件夹的内容,可以删除它。如果你在树莓派的boot分区看到了这个文件夹,很可能是由于以下原因:
- 可能挂载了某个Windows分区到了树莓派上。
- 可能使用了Windows的SD卡或者其他介质来更新或安装操作系统。
- 可能使用了某些支持Windows的工具。
2.1.5 overlays
overlays 目录在树莓派的 /boot 目录下通常包含系统启动时需要的特定设备树(device tree)文件。设备树是用来描述硬件设备信息的数据结构,可以让Linux操作系统在启动时配置硬件设备。
在树莓派的系统中,overlays目录可能包含用于配置或修改特定硬件功能的设备树文件。例如,如果你有一个外接的WiFi,你可能会有一个对应的.dtbo文件用于启动时配置WiFi。
如果你需要使用overlays目录中的某个设备树文件,你可以将它复制到 /boot/overlays/ 目录下,然后通过 dtoverlay 参数在config.txt 文件中启用它。例如,如果你有一个名为 my_wifi_dongle.dtbo 的设备树文件,你可以这样使用它:
1.将 my_wifi_dongle.dtbo 复制到 /boot/overlays/ 目录。
2.编辑 /boot/config.txt 文件,在文件中添加以下行:dtoverlay=my_wifi_dongle
3.重启树莓派以应用更改。
2.1.6 dtb
树莓派的boot目录下通常包含多个.dtb文件,这些是设备树(Device Tree)文件,用于描述CPU架构、内存布局、外设等硬件信息。.dtb文件是编译过的设备树源文件(.dts),它是一种简化的表示方法,用于描述硬件的树状结构。
.dtb文件在启动时被内核读取,以便正确配置硬件。不同的.dtb文件可以用于不同的硬件配置或用途,例如RPI3B的设备树文件对应 bcm 2710-rpi-3-b.dtb。
文件名 | 设备 |
---|---|
bcm2708-rpi-b.dtb | Pi model A and B |
bcm2708-rpi-b-plus.dtb | Pi model A+ and B+ |
bcm2709-rpi-2-b.dtb | Pi 2 model B |
bcm2710-rpi-3-b.dtb | Pi 3 model B |
bcm2708-rpi-cm.dtb | Pi Compute Module |
bcm2710-rpi-cm3.dtb | Pi Compute Module 3 |
2.1.7 bootcode.bin
bootcode.bin 文件是用于启动Raspberry Pi的引导代码。这个文件负责将计算机的CPU置于一个适合加载操作系统的状态,加载其中一个start*.elf文件。
注意: Raspberry Pi 4上未使用bootcode.bin,因为它已由板载EEPROM中的启动代码替换。
2.1.8 start*.elf
这些是二进制 (blob) 固件,加载到SoC中的VideoCore上,然后接管启动过程。
start4.elf,start4x.elf,start4cd.elf 和start4db.elf 是树莓派4的固件文件。
- start.elf: 基本的启动加载程序,用于引导Raspberry Pi
- start_x.elf: 包括相机驱动程序和编解码器
- start_db.elf: 用于固件的调试
- start_cd.elf: 简化版本,不支持编解码器和3D之类的硬件模块,并且在gpu_mem=16中指定时使用config.txt
2.1.9 fixup* .dat
这些是链接器文件,与 start*.elf 列出的文件配对。
2.1.10 kernel
文件名 | 处理器 | 树莓派型号 | 说明 |
---|---|---|---|
kernel.img | BCM2835 | Pi Zero, Pi 1 | |
kernel7.img | BCM2836, BCM2837 | Pi Zero 2 W, Pi 2, Pi 3 | 后来Pi 2使用了BCM2837 |
kernel7l.img | BCM2711 | Pi 4, Pi 400, CM4, CM4-S | 大型物理地址扩展(LPAE) |
kernel8.img | BCM2837, BCM2711, BCM2712 | Pi Zero 2 W, Pi 2, Pi 3, Pi 4, Pi 400, CM4, CM4-S, Pi 5 | 64位内核。BCM2836的Raspberry Pi 2不支持64位内核。 |
kernel_2712.img | BCM2712 | Pi 5 | Pi 5优化64位内核. |
2.1.11 cmdline.txt
cmdline.txt文件位于 boot 分区,它包含了启动时传递给内核的命令行参数,这些参数可以影响内核的启动行为。
2.1.12 config.txt
树莓派因为没有BIOS,所以Raspbian对设备的加载都是依赖在 /boot/config.txt 中的配置来加载。当Linux内核加载时,会读取 /boot/config.txt 中的设备配置和设备参数配置来把设备动态加载到Device Tree(DT)中,并且可以通过编辑它来实现多种功能,例如屏幕分辨率调整、显示输出设置、内存分配等。。
start.elf 读取存放系统配置的文件 config.txt,根据其内容设置 CPU 运行参数及内存分配情况,随后将用户代码加载至内存,启动 CPU;CPU 启动后,加载内核进行启动。
系统会先搜寻 config.txt 中参数 kernel=xxx 指定的文件作为接下来接受系统控制权的对象,树莓派默认该文件是 linux 内核,我们可以替换为用uboot 作为新一个 bootloader 然后之后再去启动 linux 内核。
如果 config.txt 里没指定 kernel 参数,则默认是先后搜寻 kernel8.img、kernel8-32.img、kernel7.img、kernel.img,分别对应 ARMv8-aarch64、ARMv8-aarch32、ARMv7 和之前版本的。
pi@raspberrypi:/boot $ sudo cat config.txt
# For more options and information see
# http://rpf.io/configtxt
# Some settings may impact device functionality. See link above for details
# uncomment if you get no picture on HDMI for a default "safe" mode
#hdmi_safe=1
# uncomment the following to adjust overscan. Use positive numbers if console
# goes off screen, and negative if there is too much border
#overscan_left=16
#overscan_right=16
#overscan_top=16
#overscan_bottom=16
# uncomment to force a console size. By default it will be display's size minus
# overscan.
#framebuffer_width=1280
#framebuffer_height=720
# uncomment if hdmi display is not detected and composite is being output
#hdmi_force_hotplug=1
# uncomment to force a specific HDMI mode (this will force VGA)
#hdmi_group=1
#hdmi_mode=1
# uncomment to force a HDMI mode rather than DVI. This can make audio work in
# DMT (computer monitor) modes
#hdmi_drive=2
# uncomment to increase signal to HDMI, if you have interference, blanking, or
# no display
#config_hdmi_boost=4
# uncomment for composite PAL
#sdtv_mode=2
#uncomment to overclock the arm. 700 MHz is the default.
#arm_freq=800
# Uncomment some or all of these to enable the optional hardware interfaces
#dtparam=i2c_arm=on
#dtparam=i2s=on
#dtparam=spi=on
# Uncomment this to enable infrared communication.
#dtoverlay=gpio-ir,gpio_pin=17
#dtoverlay=gpio-ir-tx,gpio_pin=18
# Additional overlays and parameters are documented /boot/overlays/README
# Enable audio (loads snd_bcm2835)
dtparam=audio=on
# Automatically load overlays for detected cameras
camera_auto_detect=1
# Automatically load overlays for detected DSI displays
display_auto_detect=1
# Enable DRM VC4 V3D driver
dtoverlay=vc4-kms-v3d
max_framebuffers=2
# Disable compensation for displays with overscan
disable_overscan=1
[cm4]
# Enable host mode on the 2711 built-in XHCI USB controller.
# This line should be removed if the legacy DWC2 controller is required
# (e.g. for USB device mode) or if USB support is not required.
otg_mode=1
[all]
[pi4]
# Run as fast as firmware / board allows
arm_boost=1
[all]
2.2 树莓派的boot启动过程
为了降低成本,树莓派省去了传统计算机用来存储引导加载程序的板载存储器(BIOS), 直接把引导程序放在了SD卡中。
当启动时,ARM Cortex-A53的CPU会处于复位状态,由VideoCore IV GPU核心负责启动系统。(所以大部分boot的启动都是由GPU code来完成,而不是CPU)。
树莓派的官网上也提供引导程程序的精简版本(fixup_cd.dat,start_cd.elf 用于GPU内存只有16MB的时候,会损失部分CPU特性)和测试版本(fixup_x.dat, start_x.elf 这种版本可以使用额外的video codes)
由于这种写死的程序加上从SD卡开始引导,这就让树莓派不会因为软件的原因变成砖头的(除非硬件损坏)。
- 1.从系统芯片中加载第一阶段的启动程序,这个启动程序负责挂载在SD卡中的FAT32的文件系统,从而让他可以启动第二阶段的boot(bootcode.bin),这部分程序是写死在在芯片中的,所以不能修改。
- 2.bootcode.bin 则用来从SD卡上检索GPU固件(start.elf),然后运行它,从而启动GPU
- 3.start.elf 启动后,读取存放系统配置的文件config.txt。当config.txt文件被加载解析之后, start.elf 会读取 cmdline.txt 和 kernel8.img. cmdline.txt 包含内核运行的参数,而kernel8.img将会被加载到处理器分配的共享内存中,当内核加载成功,处理器将结束复位状态,内核开始正式运行,系统启动正式开始。
- 4.start.elf 除了上面的,也会传递一些额外的参数给内核,比如:DMA通道,GPU参数,MAC地址,eMMC时钟速度、内核寻址范围等等
2.3 Systemd进程启动 —— init进程
内核启动之后的第一个进程就是 init进程。我们看看树莓派里面的init进程是什么?
采用systemd作为默认的init系统,后期版本大多数是 /lib/systemd/systemd。
`pi@raspberrypi:~ $ ls -al /usr/sbin/init
lrwxrwxrwx 1 root root 20 May 13 2023 /usr/sbin/init -> /lib/systemd/systemd
在sysvinit中有明确定义的运行级别(如:0、1、3、5、6)与systemd中特定的 目标 存在一一对应的关系。然而,对于用户自定义运行级别(2、4)却没有。
如需要同样功能,建议你以原有运行级别所对应的systemd目标为基础,新建一个/etc/systemd/system/<目标名>.target(可考
/usr/lib/systemd/system/graphical.target), 然后创建目录/etc/systemd/system/<目标名>.wants,并向其中加入需启用的服务链接(指向/ur/lib/systemd/system/)。
pi@raspberrypi:/lib/systemd/system $ ls -al | grep runlevel
lrwxrwxrwx 1 root root 15 May 13 2023 runlevel0.target -> poweroff.target
lrwxrwxrwx 1 root root 13 May 13 2023 runlevel1.target -> rescue.target
drwxr-xr-x 2 root root 4096 Aug 7 2021 runlevel1.target.wants
lrwxrwxrwx 1 root root 17 May 13 2023 runlevel2.target -> multi-user.target
drwxr-xr-x 2 root root 4096 Aug 7 2021 runlevel2.target.wants
lrwxrwxrwx 1 root root 17 May 13 2023 runlevel3.target -> multi-user.target
drwxr-xr-x 2 root root 4096 Aug 7 2021 runlevel3.target.wants
lrwxrwxrwx 1 root root 17 May 13 2023 runlevel4.target -> multi-user.target
drwxr-xr-x 2 root root 4096 Aug 7 2021 runlevel4.target.wants
lrwxrwxrwx 1 root root 16 May 13 2023 runlevel5.target -> graphical.target
drwxr-xr-x 2 root root 4096 Aug 7 2021 runlevel5.target.wants
lrwxrwxrwx 1 root root 13 May 13 2023 runlevel6.target -> reboot.target
-rw-r--r-- 1 root root 805 May 13 2023 systemd-update-utmp-runlevel.service
pi@raspberrypi:/lib/systemd/system $
查看当前的运行级别,可以使用命令:
systemctl get-default
pi@raspberrypi:~ $ systemctl get-default
graphical.target
pi@raspberrypi:~ $
2.3.1 Systemd应用原理—— 运行级别(graphical.target[默认])
pi@raspberrypi:/etc/systemd/system $ cat /lib/systemd/system/graphical.target
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Graphical Interface
Documentation=man:systemd.special(7)
Requires=multi-user.target
Wants=display-manager.service
Conflicts=rescue.service rescue.target
After=multi-user.target rescue.service rescue.target display-manager.service
AllowIsolate=yes
pi@raspberrypi:/etc/systemd/system $
[Unit] 区块:启动顺序与依赖关系
- Description 字段给出当前服务的简单描述
- Documentation 字段给出文档位置
- Requires 字段则表示multi-user.target 和"强依赖 "关系,即如果该服务启动失败或异常退出,那么也必须退出
- Wants 字段表示和 display-manager.service 存在"弱依赖 "(该服务启动失败不影响继续执行)关系
- Conflicts 字段表示冲突字段。如果rescue.service 或rescue.target 正在运行,graphical.target就不能运行,反之亦然
- After 字段表示如果multi-user.target、rescue.service、rescue.target、display-manager.service 需要启动,那么graphical.target 应该在它们之后启动
- AllowIsolate 允许使用systemctl isolate 命令切换到graphical.target
也就是说这里强烈依赖了 multi-user.target 。之后才会执行display-manager.service 服务。我们看看关联 graphical.target 有哪些服务?
pi@raspberrypi:/etc/systemd/system/graphical.target.wants $ ls -al
total 8
drwxr-xr-x 2 root root 4096 Jan 28 2022 .
drwxr-xr-x 19 root root 4096 Jan 16 14:05 ..
lrwxrwxrwx 1 root root 35 Jan 28 2022 udisks2.service -> /lib/systemd/system/udisks2.service
pi@raspberrypi:/etc/systemd/system/graphical.target.wants $
2.3.2 Systemd应用原理—— 运行级别(multi-user.target)
pi@raspberrypi:~ $ cat /lib/systemd/system/multi-user.target
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Multi-User System
Documentation=man:systemd.special(7)
Requires=basic.target
Conflicts=rescue.service rescue.target
After=basic.target rescue.service rescue.target
AllowIsolate=yes
pi@raspberrypi:~ $
这里又依赖于了 basic.target。我们看看关联 multi-user.target 有哪些服务?
pi@raspberrypi:/etc/systemd/system/multi-user.target.wants $ ls -al
total 8
drwxr-xr-x 2 root root 4096 Jan 18 09:07 .
drwxr-xr-x 19 root root 4096 Jan 16 14:05 ..
lrwxrwxrwx 1 root root 40 Jan 28 2022 avahi-daemon.service -> /lib/systemd/system/avahi-daemon.service
lrwxrwxrwx 1 root root 41 Jan 28 2022 console-setup.service -> /lib/systemd/system/console-setup.service
lrwxrwxrwx 1 root root 32 Jan 28 2022 cron.service -> /lib/systemd/system/cron.service
lrwxrwxrwx 1 root root 34 Jan 28 2022 dhcpcd.service -> /lib/systemd/system/dhcpcd.service
lrwxrwxrwx 1 root root 42 Jan 28 2022 dphys-swapfile.service -> /lib/systemd/system/dphys-swapfile.service
lrwxrwxrwx 1 root root 38 Jan 28 2022 networking.service -> /lib/systemd/system/networking.service
lrwxrwxrwx 1 root root 37 Jan 28 2022 nfs-client.target -> /lib/systemd/system/nfs-client.target
lrwxrwxrwx 1 root root 48 Jan 28 2022 raspberrypi-net-mods.service -> /lib/systemd/system/raspberrypi-net-mods.service
lrwxrwxrwx 1 root root 36 Jan 28 2022 remote-fs.target -> /lib/systemd/system/remote-fs.target
lrwxrwxrwx 1 root root 45 Jan 28 2022 rpi-eeprom-update.service -> /lib/systemd/system/rpi-eeprom-update.service
lrwxrwxrwx 1 root root 33 Jan 28 2022 rsync.service -> /lib/systemd/system/rsync.service
lrwxrwxrwx 1 root root 35 Jan 28 2022 rsyslog.service -> /lib/systemd/system/rsyslog.service
lrwxrwxrwx 1 root root 31 Jan 16 13:49 ssh.service -> /lib/systemd/system/ssh.service
lrwxrwxrwx 1 root root 37 Jan 28 2022 sshswitch.service -> /lib/systemd/system/sshswitch.service
lrwxrwxrwx 1 root root 40 Jan 28 2022 triggerhappy.service -> /lib/systemd/system/triggerhappy.service
lrwxrwxrwx 1 root root 34 Jan 18 09:07 vsftpd.service -> /lib/systemd/system/vsftpd.service
lrwxrwxrwx 1 root root 42 Jan 28 2022 wpa_supplicant.service -> /lib/systemd/system/wpa_supplicant.service
pi@raspberrypi:/etc/systemd/system/multi-user.target.wants $
这里就看到了我们之前配置的一系列自启动服务
- ssh.service ssh登录相关
- vstftp.service ftp服务相关
- nfs-client.target nfs服务相关
- …
2.3.3 Systemd应用原理—— 运行级别(basic.target)
pi@raspberrypi:/etc/systemd/system $ cat /lib/systemd/system/basic.target
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Basic System
Documentation=man:systemd.special(7)
Requires=sysinit.target
Wants=sockets.target timers.target paths.target slices.target
After=sysinit.target sockets.target paths.target slices.target tmp.mount
# We support /var, /tmp, /var/tmp, being on NFS, but we don't pull in
# remote-fs.target by default, hence pull them in explicitly here. Note that we
# require /var and /var/tmp, but only add a Wants= type dependency on /tmp, as
# we support that unit being masked, and this should not be considered an error.
RequiresMountsFor=/var /var/tmp
Wants=tmp.mount
pi@raspberrypi:/etc/systemd/system $
它又依赖于 sysinit.target
2.3.4 Systemd应用原理—— 运行级别(sysinit.target)
pi@raspberrypi:/etc/systemd/system $ cat /lib/systemd/system/sysinit.target
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=System Initialization
Documentation=man:systemd.special(7)
Conflicts=emergency.service emergency.target
Wants=local-fs.target swap.target
After=local-fs.target swap.target emergency.service emergency.target
pi@raspberrypi:/etc/systemd/system $
我们看看关联sysinit.target有哪些服务?
pi@raspberrypi:/etc/systemd/system/sysinit.target.wants $ ls -al
total 8
drwxr-xr-x 2 root root 4096 Jan 28 2022 .
drwxr-xr-x 19 root root 4096 Jan 16 14:05 ..
lrwxrwxrwx 1 root root 40 Jan 28 2022 fake-hwclock.service -> /lib/systemd/system/fake-hwclock.service
lrwxrwxrwx 1 root root 42 Jan 28 2022 keyboard-setup.service -> /lib/systemd/system/keyboard-setup.service
lrwxrwxrwx 1 root root 42 Jan 28 2022 systemd-pstore.service -> /lib/systemd/system/systemd-pstore.service
lrwxrwxrwx 1 root root 45 Jan 28 2022 systemd-timesyncd.service -> /lib/systemd/system/systemd-timesyncd.service
pi@raspberrypi:/etc/systemd/system/sysinit.target.wants $
2.3.5 Systemd应用原理—— 运行级别(rc-local.service)
自从很多Linux发行版采用systemd来管理服务和开机启动程序后,开机速度大大加快。同时老的开机启动脚本/etc/rc.local默认不再被支持。然而很多时候rc.local自启动脚本带来的方便和快捷胜过了速度需求,此时我们希望找回常用的rc.local功能。
树莓派为了兼容这个,还是保留了。
我们也可以在这里加一些自启动脚本,当然不是很建议。还是直接用新技术把。
pi@raspberrypi:/usr/lib/systemd/system $ cat rc-local.service
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
# This unit gets pulled automatically into multi-user.target by
# systemd-rc-local-generator if /etc/rc.local is executable.
[Unit]
Description=/etc/rc.local Compatibility
Documentation=man:systemd-rc-local-generator(8)
ConditionFileIsExecutable=/etc/rc.local
After=network.target
[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
RemainAfterExit=yes
GuessMainPID=no
pi@raspberrypi:/usr/lib/systemd/system $
3.上电启动流程图
栈区(stack)
栈区介绍
1.栈区与堆区是相互毗邻的,并且生长方向相反;当栈指针触及到堆指针时,意味着栈空间已经被耗尽(如今地址空间越来越大,及虚拟内存技术发展,栈与堆可能放置在内存的任何地方,但生长方向依然还是相向的)。
2.栈区域包含一个LIFO结构的程序栈,其通常放置在内存的高地址处,栈区按内存地址由高到低方向生长,其最大大小由编译时确定,速度快,但自由性差,最大空间不大。
3.栈指针寄存器跟踪栈顶位置,每当有数值被压入栈中,栈顶指针会被调整,在一个函数的调用过程中,压入的一系列数值被称作“栈帧”,栈帧至少包含一个返回地址。
存放内容
1.临时创建和const定义的局部变量存放在栈区。
2.函数调用和返回时,其入口参数和返回值存放在栈区。
堆区(heap)
堆区介绍
堆区通常用作动态内存分配,堆空间起始于BSS段的末尾,并按内存地址由低到高方向生长,其大小由系统内存/虚拟内存上限决定,速度较慢,但自由性大,可用空间大。返回地址。
存放内容
1.堆区用于存放程序运行中被动态分布的内存段,可增可减。
2.可以用malloc等函数实现动态分布内存。当有malloc函数分配的内存时必须用free函数进行内存释放,否则会造成内存泄漏。
全局区(静态区)
全局区介绍
和栈区一样,通常是用于那些在编译期间就能确定存储大小的变量的存储区,但它用于的是在整个程序运行期间都可见的全局变量和静态变量。全局区有.bss段和.data段组成,可读可写。
.bss段
1.未初始化的全局变量存放在.bss段。
2.初始化为0的全局变量和初始化为0的静态变量存放在.bss段。
3…bss段不占用可执行文件空间,其内容有操作系统初始化。.data段
1.已经初始化的全局变量存放在.data段。
2.静态变量存放在.data段。
3. .data段占用可执行文件空间,其内容有程序初始化。
4.const定义的全局变量存放在.rodata段。
常量区
1.字符串、数字等常量存放在常量区。
2.程序运行期间,常量区的内容不可以被修改。
代码区
1.程序执行代码存放在代码区,其值不能修改(若修改则会出现错误)。
2.字符串常量也有可能存放在代码区。