树莓派上的阿尔派启动过程

今天我们将对Raspberry Pi上的Alpine Linux启动过程进行详细了解。

clip_image001

图片显示的是一张带有Alpine图像的 "处女 "SD卡的内容,它还没有被启动。

clip_image003

这张图片显示了启动文件夹的内容。

树莓派的初始启动阶段

请参考这个链接,了解不同启动媒体的深入指南。

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的信息。

内核和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

这将加载模块

  • 循环
    • 循环设备被用来将文件挂载为文件系统
  • 壁球
  • ǞǞǞ
    • SCSI磁盘驱动器/块驱动器
    • 显然是从Alpine的默认配置中复制的,例如这里。 https://wiki.alpinelinux.org/wiki/Create_a_Bootable_Compact_Flash
  • 储存器
    • Linux的USB大容量存储驱动器

注意:模块名称可能同时包含-和_,这些符号显然可以互换。

安静

  • 将默认的内核日志级别设置为KERN_WARNING,在启动过程中,除了非常严重的日志信息,其他的都会被抑制(来源:Raspberry Pi 文档)。

dwc_otg.lpm_enable=0

console=tty1

  • 定义了串行控制台

进一步阅读。

初步统计

"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)。

clip_image005

这第二个文件打包了一个文件夹和文件结构,如果你以前使用过Linux,你会认识到这一点。它是以一种特殊的格式,即"cpio",这与焦油相似。

clip_image007

如果你感到好奇,你可以在这里阅读更多关于cpio的信息。

https://en.wikipedia.org/wiki/Cpio

这就是文件的内容。

clip_image008

在bin,它包含。

  • 忙碌的盒子
  • 衬衫

在sbin中,它包含

倡议 文件被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
  • ...和sysfs到/sys
  • 解析通过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 被执行以找到启动媒体。
  • 如果参数 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。

clip_image010

modloop将在dev-mount之后启动,而在checkfs、fsck、hwdriver、modules、hwclock、dev、sysfs之前。

hwdrivers将在modloop之后启动,hwdrivers将使用modprobe加载驱动程序。

运行级别启动。

clip_image012

运行级别默认。

clip_image014

モードロック

还有modloop文件(modloop-rpi或modloop-rpi2)。

如何安装

你的系统的正确文件是在脚本/etc/init.d/modloop的函数find_modloop()中确定的。

的结果是 uname -r 的目录进行检查 - 适当命名的子目录需要在其中。

比如说。 4.14.69-0-rpi2

这样,你的内核就会使用正确的模块(uname -r 打印内核版本)。

modloop被挂载在目录 /.modloop

clip_image015

符号链接是由 /lib/firmware/lib/modules/firmware,以及 /lib/modules 是符号链接到 /.modloop/modules

提示: 如果你想给Raspberry Pi 3B+ WiFi固件打补丁,你必须把它放到 模态循环.

Modloop的内容

要探索modloop的内容,你可以使用(在薄荷系统上测试)。

apt-get 安装 squashfs-tools

撤销quashfs modloop-rpi2

它包含固件和驱动程序。

clip_image016

clip_image017

参考文献。

进一步阅读。