树莓派上的阿尔派启动过程
今天我们将对Raspberry Pi上的Alpine Linux启动过程进行详细了解。
图片显示的是一张带有Alpine图像的 "处女 "SD卡的内容,它还没有被启动。
这张图片显示了启动文件夹的内容。
树莓派的初始启动阶段
请参考这个链接,了解不同启动媒体的深入指南。
https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/bootflow.md
Raspberry Pi没有像基于x86的IBM PC兼容计算机那样的经典 "BIOS"。它的CPU应该被认为是VideoCore IV的附加设备,用于启动目的,而不是相反。
因此,最初VideoCore IV,即GPU,拥有控制权。它从自己的ROM开始(第一阶段),并检查启动源中的一个文件,名为 bootcode.bin.为了我们的目的,我们将假设该文件是从SD卡中加载的。
bootcode.bin (51 KB) 被加载到GPU的本地128K L2缓存中。它启用RAM并加载第三阶段:start.elf (2759 KB)
start.elf将读取config.txt,并为VideoCore IV设置适当的设置。config.txt是Raspberry Pi的一个参数文件,你可以在其中设置视频模式、过扫描、音频选项、MPEG编解码器、超频设置等。- start.elf本身是GPU的固件,是它自己的操作系统,称为 "VideoCore OS"。
注意:start.elf有不同的版本,包括start_x.elf和start_cd.elf--这是start.elf的一个缩减版本,用于GPU内存减少到最大时:gpu_mem=16。后面这个版本可能对嵌入式系统很有意思。更多信息请参考这个网站:https://www.raspberrypi.org/documentation/configuration/boot_folder.md
config.txt是可选的。如果你在几个Pi上使用同一张SD卡(或图像),你可以设置条件过滤器,以适用于某些情况;你甚至可以根据Raspberry Pi的序列来应用一个条件。 或一个GPIO的状态.也有可能在config.txt中包含其他文件。总的来说,你可以得到一个相当强大的系统来完成Raspberry Pi的基本配置。
当然,在config.txt中也有启动选项,控制进一步启动的方式。
start.elf然后加载内核(由config.txt/defaults指定),并以cmdline.txt作为内核命令行启动它。
config.txt 启动选项
看一看。
https://www.raspberrypi.org/documentation/configuration/config-txt/boot.md
来阅读更多关于可用的启动选项。
Alpine的默认config.txt
让我们看看阿尔派恩的3.8.1 ARMHF版本中的config.txt。
- disable_splash=1
- 这将使启动时的彩虹屏失效(纯粹是外观上的)。
- boot_delay=0
- 在加载内核之前不要在start.elf中等待。(对于一些SD卡来说,启动延迟可能是必要的,以使其 "准备好")
- gpu_mem=256
- 设置GPU和CPU之间的内存分配。Pi的内存是共享的。这里我们为GPU设置了256MB,你可能想在不驱动显示器的嵌入式系统上减少这个容量。
- gpu_mem_256=64
- 为总内存只有256MB的Pi设置内存的分割(只有一些老一代的Pi 1B)。
- [pi0]
- Pi Zero的条件性声明;这包括Pi Zero和Pi Zero W
- 只有当我们在Pi Zero/Zero W上运行时,所有的声明才会被应用,直到下一个条件。
- Pi Zero / Zero W仍然使用旧的ARMv6 ISA CPU(ARM11 @ 1 GHz)。
- 我们将使用一个为ARMv6编译的内核
- kernel=boot/vmlinuz-rpi
- 这指定了要加载的内核
- 这必须是一个未压缩的内核镜像文件
- 32位内核默认被加载到地址0x8000。
- initramfs boot/initramfs-rpi
- "initramfs命令同时指定了ramfs文件名和加载它的内存地址"
- initramfs = 初始RAM文件系统
- 这个文件可以用7Zip提取,例如(提取两次)。
- 注意:这个配置参数在指定时没有"="。
- [pi1]
- 这将在所有Raspberry Pi 1上应用以下配置行 - 在Pi 2上将不匹配,等等。
- kernel=boot/vmlinuz-rpi
- initramfs boot/initramfs-rpi
- [pi2]
- kernel=boot/vmlinuz-rpi2
- 注意我们如何使用不同的内核,因为在这里我们有一个可以运行ARMv7代码的CPU
- initramfs boot/initramfs-rpi2
- 和一个不同的initramfs ...
- [pi3]
- kernel=boot/vmlinuz-rpi2
- initramfs boot/initramfs-rpi2
- [pi3+]
- kernel=boot/vmlinuz-rpi2
- initramfs boot/initramfs-rpi2
- [全部]
- 下面这一行将再次适用于所有的Pi's
- 包括usercfg.txt
- 将包括一个文件usercfg.txt到配置中,这在默认的Alpine镜像中还不存在。
- 在这里,你可以把你想要的额外的自定义配置,例如HDMI分辨率,等等。
- 或者,你可以在config.txt中粘贴以下内容
注意:Alpine中使用的配置没有为initramfs指定第二个参数(挂载initramfs的地址);因此不建议1:1复制。这就是说,它似乎是有效的,可能默认为安全的默认值!
阅读更多关于config.txt的信息。
- https://www.raspberrypi.org/documentation/configuration/config-txt/README.md
- https://www.raspberrypi.org/documentation/configuration/config-txt/boot.md
- https://www.raspberrypi.org/documentation/configuration/config-txt/conditional.md
- https://www.raspberrypi.org/documentation/configuration/config-txt/misc.md
- https://elinux.org/RPiconfig
- https://www.raspberrypi.org/forums/viewtopic.php?f=63&t=10532
- https://raspberrypi.stackexchange.com/questions/49980/raspbian-kernel-and-initramfs
内核和Initramfs
内核
根据config.txt的设置,正确的内核将被选择并加载到RAM中。一旦加载完毕,ARM处理器内核(被/被)从复位中释放出来,因此(它/它们)可以启动内核。
start.elf还将传递它自己的额外参数,例如设置DMA通道、SMSC LAN芯片的MAC地址等。
获取 cmdline.txt:
cat /proc/cmdline
dmesg | grep "命令行"
cmdline.txt
让我们看一下Alpine的默认cmdline.txt。
modules=loop,squashfs,sd-mod,usb-storage quiet dwc_otg.lpm_enable=0 console=tty1
这将加载模块
- 循环
- 循环设备被用来将文件挂载为文件系统
- 壁球
- 适用于Linux的压缩的读-ony文件系统
- 特别适用于嵌入式系统
- https://en.wikipedia.org/wiki/SquashFS
- 这是在后面的modloop中使用的
- ǞǞǞ
- SCSI磁盘驱动器/块驱动器
- 显然是从Alpine的默认配置中复制的,例如这里。 https://wiki.alpinelinux.org/wiki/Create_a_Bootable_Compact_Flash
- 储存器
- Linux的USB大容量存储驱动器
注意:模块名称可能同时包含-和_,这些符号显然可以互换。
安静
- 将默认的内核日志级别设置为KERN_WARNING,在启动过程中,除了非常严重的日志信息,其他的都会被抑制(来源:Raspberry Pi 文档)。
dwc_otg.lpm_enable=0
- USB控制器驱动程序,通过此设置禁用USB链接电源管理。(这是推荐的默认值)
- https://elinux.org/RPI_BCM2708_Parameters#module:_sdhci-bcm2708
console=tty1
- 定义了串行控制台
进一步阅读。
- https://elinux.org/RPi_cmdline.txt
- https://www.raspberrypi.org/documentation/configuration/cmdline-txt.md
初步统计
"initramfs命令同时指定了ramfs文件名和加载它的内存地址。它在一个参数中完成了ramfsfile和ramfsaddr的操作。地址也可以是 followkernel (或 0),把它放在内核镜像之后的内存中。示例值是:initramfs initramf.gz 0x00800000 或 initramfs init.gz followkernel。注意:这个选项使用的语法与其他选项不同,你不应该在这里使用=字符。"- Raspberry Pi 文档
在dmesg中会有一个类似"[ 0.143709] Trying to unpack rootfs image as initramfs... "的内核信息,表明镜像正在被解包。
initramfs允许Linux内核添加模块,并在准备安装实际文件系统时执行其他任务。因此,它是一个非常通用的启动系统,不需要重新编译内核/增加内核的开销。例如,对于使用加密文件系统的用户,initramfs在解密和挂载文件系统之前会要求提供密码。
根据你的Raspberry Pi的CPU,将使用不同的initramfs文件,如下面的设置 config.txt:
- boot/initramfs-rpi用于Pi 1 / 1B+和Pi Zero / Zero W,以及计算模块1
- boot/initramfs-rpi2用于所有其他的Pi(2, 3B, 3B+, ...)
initramfs文件可以在Windows 7Zip上解压,比如说。(你必须解压两次:第一个文件包含一个压缩的第二个文件)
initramfs图像可以是不同的格式,取决于哪些算法被静态编译到内核中(例如gzip / bzip2 / LZMA / XZ / LZO / LZ4)。
这第二个文件打包了一个文件夹和文件结构,如果你以前使用过Linux,你会认识到这一点。它是以一种特殊的格式,即"cpio",这与焦油相似。
如果你感到好奇,你可以在这里阅读更多关于cpio的信息。
https://en.wikipedia.org/wiki/Cpio
这就是文件的内容。
在bin,它包含。
- 忙碌的盒子
- 紧凑的可执行文件,在一个应用程序中包含几个UNIX实用程序
- 特别是针对嵌入式系统
- https://busybox.net/about.html
- 衬衫
在sbin中,它包含
- apk
- 阿尔卑斯山软件包管理工具
- 靴印
- 这是一个对启动时间进行分析的工具
- https://elinux.org/Bootchart
- http://www.bootchart.org/
- nlplug-findfs
- 用于挂载块设备并搜索可用的apkovl,如果内核命令行中没有传递apkovl=的话
- https://github.com/alpinelinux/mkinitfs
的 倡议 文件被Linux内核启动为 第一个过程,基本上是设置了阿尔卑斯山。
这里是用于生成Alpine的initramfs的工具。
https://github.com/alpinelinux/mkinitfs
倡议
这是一个连续的 摘录 从initramfs中启动的init脚本所做的事情。
- 创建目录/usr/bin, /usr/sbin, /proc, /sys, /dev, /sysroot , /media/cdrom, /media/usb, /tmp, /run递归。
- 其中一些目录似乎已经存在于initramfs中了。
- 安装busybox(设置指向busybox嵌入式命令的符号链接)。
- 将PATH设置为/usr/bin:/bin:/usr/sbin:/sbin
- 使用mknod创建/dev/null
- 将proc挂载到/proc
- proc是一个进程信息伪文件系统
- 你可以通过阅读获得关于内核的信息,并通过写入这个目录中的一些文件来配置某些东西
- 编号的条目是包含有关这个特定进程ID的进程信息的目录。
- https://www.tldp.org/LDP/Linux-Filesystem-Hierarchy/html/proc.html
- ...和sysfs到/sys
- 这是关于硬件设备、设备驱动和内核子系统的信息。
- https://en.wikipedia.org/wiki/Sysfs
- 解析通过cmdline.txt传递的选项,可以识别以下内容。
- alpine_dev autodetect autoraid chart cryptroot cryptdm cryptheader cryptoffset
- cryptdiscards cryptkey debug_init dma init_args keep_apk_new modules ovl_dev
- pkgs quiet root_size root usbdelay ip alpine_repo apkovl alpine_start splash
- 黑名单 overlaytmpfs rootfstype rootflags nbd resume s390x_net dasd ssh_key
提示:尝试改变"安静将cmdline.txt中的""改为"不安静"来获得更多的启动信息,并看到来自init的许多信息
- 将devtmpfs挂载到/dev
- 加载驱动程序,以便以后能够安装modloop。
- 如果参数nbd存在并已配置(网络块设备),则尝试调出一个IP地址,并使用nbd-client配置网络块设备。
- 如果根参数被设置为
- if [ -n "$KOPT_root"]; then
- –如果 "$KOPT_root "的长度为非零,则对n进行评估。
- 然后执行nlplug-findfs
- overlaytmpfs是可以选择处理的
- 当前的挂载点被迁移到/sysroot。
- switch_root被运行,控制权被传递给/sbin/init
- (这是一个在常规模式下安装Alpine Linux的入口,而不是在只读模式下。)
否则的话。
- nlplug-findfs 被执行以找到启动媒体。
- 在这里可以进行加密选择和处理
- 发现apkovls的路径被添加到 /tmp/apkovl (使用-a开关,见 https://pi3g.com/2019/01/09/nlplug-findfs-documentation/)
- 显然,只有在找到apkovl的情况下才会创建该文件。
- https://github.com/alpinelinux/mkinitfs/blob/master/nlplug-findfs.c
- apkovls正在寻找匹配的 "*.apkovl.tar.gz*"。 这就是为什么lbu commit创建的备份文件是 不是 自动找到/应用--它们有一个结构,即 主机名.时间戳.tar.gz, 例如:pidoctor.20190110180745.tar.gz
- 如果参数 apkovl 被设定。
- 而它是空的 - 并且/tmp/apkovls存在(即文件被nlplug-findfs发现)--ovl被设置为 从第一行开始 的/tmp/apkovls
- 并以http:// / https:// / ftp:// 开始--试图通过网络连接来拉动apkovl。
- 否则,ovl被设置为apkovl选项中的字符串。
- 如果apkovl存在的话。
- 并且是一个.gz文件,apkovl被解包了 tar -C "$dest" -zxvf "$ovl" > $ovlfiles 而/tmp/ovlfiles被填上了tar的输出。
- 有代码可用于闪屏,bsplash是包含在busybox中的一个函数--它显示bsplash*.ppm文件。
- if [ -z "$ALPINE_REPO" ] -> 表示,如果字符串$ALPINE_REPO是空的
- 在某些情况下,为运行级别sysinit、boot、shutdown和default(例如firstboot服务)添加了默认的bootservices。
- apk库被添加和扩展
- alpine-base软件包依赖于alpine-baselayout、alpine-conf和其他一些软件包。
- 它还包括文件/etc/os-release ->,它显示了Alpine Linu的名称、版本和漂亮的名字。
- alpine-baselayout是基础目录布局。
- alpine-conf包括,除其他外,lbu脚本和设置脚本。
- busybox包括busybox二进制文件
- busybox-initscripts包括一些initscripts
- 最后,切换了根目录,并启动了/sbin/init。
- 这将启动OpenRC并执行运行级别。
- /sbin/init是一个指向/bin/busybox的符号链接。
请注意:apkovl在modloop和安装软件包前被安装。这允许apkovl配置哪些软件包将被安装,以及其他事项。
开放式RC运行级别
sysinit -> boot -> default -> shutdown
https://hoverbear.org/2014/09/29/init-runlevels-and-targets/
运行级别sysinit。
modloop将在dev-mount之后启动,而在checkfs、fsck、hwdriver、modules、hwclock、dev、sysfs之前。
hwdrivers将在modloop之后启动,hwdrivers将使用modprobe加载驱动程序。
运行级别启动。
运行级别默认。
モードロック
还有modloop文件(modloop-rpi或modloop-rpi2)。
如何安装
你的系统的正确文件是在脚本/etc/init.d/modloop的函数find_modloop()中确定的。
的结果是 uname -r 的目录进行检查 - 适当命名的子目录需要在其中。
比如说。 4.14.69-0-rpi2
这样,你的内核就会使用正确的模块(uname -r 打印内核版本)。
modloop被挂载在目录 /.modloop
符号链接是由 /lib/firmware 至 /lib/modules/firmware,以及 /lib/modules 是符号链接到 /.modloop/modules
提示: 如果你想给Raspberry Pi 3B+ WiFi固件打补丁,你必须把它放到 模态循环.
Modloop的内容
要探索modloop的内容,你可以使用(在薄荷系统上测试)。
apt-get 安装 squashfs-tools
撤销quashfs modloop-rpi2
它包含固件和驱动程序。
参考文献。
- https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/bootflow.md
- https://raspberrypi.stackexchange.com/questions/10442/what-is-the-boot-sequence
- https://wiki.alpinelinux.org/wiki/Alpine_Linux:Overview
- https://pi-ltsp.net/advanced/kernels.html
- https://www.raspberrypi.org/documentation/configuration/boot_folder.md
- https://wiki.beyondlogic.org/index.php?title=Understanding_RaspberryPi_Boot_Process
- https://thekandyancode.wordpress.com/2013/09/21/how-the-raspberry-pi-boots-up/