引导加载程序
boot loader
将内核加载到内存中,并将控制权转交到内核的启动代码。引导加载程序协议的详细信息不在此处描述。Zircon使用的
boot loader
以Zircon Boot Image的格式加载内核镜像与数据块(data blob)。
ZBI
格式是嵌入了
boot loader
传递的参数项的容器格式,包括硬件特定信息、提供引导选项的[kernel “command line”](kernel_cmdline.md)和RAM Disk镜像(通常是压缩格式的)。内核在启动的早期阶段提取了一些供它自身使用的基本信息。
ZBI
中嵌入的项目之一是初始RAM Disk文件系统镜像。其通常使用
LZ4
压缩格式。一旦解压缩,镜像将采用
BOOTFS
格式。这是一个小型的只读文件系统格式,简单的列出了文件名,以及每个文件在BOOTFS镜像中的偏移量和大小(两个值都必须以页面对齐,限制为32位)。
主BOOTFS镜像包含用户空间系统需要运行的所有:可执行文件、共享库和数据文件。这些当中包括设备驱动程序和更高级的文件系统的实现,使从存储设备或网络中读取更多代码和数据成为可能。
系统自启动后,主BOOTFS镜像中的文件作为只读文件系统挂载到
/boot
目录下(并由bootsvc管理)。
内核不包含任何用于解压缩LZ4格式的代码,也不包含能够解释BOOTFS格式的任何代码。相反,所有这些工作由第一个用户空间进程
userboot
完成。
userboot
是一个常规的用户空间进程。它只能像其它进程一样,通过
vDSO
请求标准的系统调用,以及完全受到
vDSO enforcement
的约束。
userboot
的特殊之处在于它的加载方式。
userboot
程序以ELF动态共享对象格式生成,使用与vDSO相同的
RODSO layout
布局。类似于vDSO,
userboot
ELF镜像在编译时嵌入到的内核中。它的简单布局意味着要加载它不需要内核在引导时解释ELF头部信息。内核只需要知道三件事:只读段的大小、可执行段的大小和
userboot
执行入口点。在编译时,由
userboot
程序的ELF镜像中提取这些值,在内核代码中当中常量使用。
像其它进程一样,
userboot
必须在执行前确保VDSO已经映射到其地址空间,以便进行系统调用。内核将
userboot
和vDSO映射到第一个用户进程中,然后在
userboot
入口点启动它。
在常规
program loading
,过程中,发送一个
bootstrap message
到新进程中。进程的首个线程接收一个位于寄存器中的
channel
句柄。接下来可以读取进程创建者发送的数据和句柄。
内核使用与以上完全相同的协议来启动
userboot
。内核命令行被拆分为单独的词,这些词将成为引导消息中的环境变量字符串。
userboot
本身需要的所有句柄,以及系统的其余部分访问内核设施所需的句柄,都包含在此消息中。按照正常格式,
handle info enties
描述每个句柄的用途。其中包括
PA_VMO_VDSO
句柄
。
用于通知新进程VDSO映射的
standard convention
标准,要求进程来解释vDSO的ELF头部信息和符号表,用于定位系统调用入口点。为了避免这种复杂性,
userboot
以一种不同的方式在vDSO中查找系统调用入口点。
当内核将
userboot
映射到第一个用户进程时,它选择内存中的一个随机位置,就像正常程序加载时一样。但是,当它映射vDSO时,它不会像在通常情况下选择另一个随机的位置。相反,它会将vDSO镜像正好放置在内存中的
userboot
镜像之后。这样,vdDSO代码总是位于
userboot
代码的固定偏移处。
在编译时,所有系统调用入口点的符号表条目都将由vDSO的ELF镜像中提取出来。然后,发送给链接器脚本符号定义,由于每个符号在vDSO镜像中的偏移是固定的,使用该偏移来定义位于链接器提供的
_end
符号的固定偏移符号。这样,
userboot
代码可以直接调用每个vDSO入口点,因为这些入口将在
userboot
镜像本身之后的正确内存位置中。
userboot
做的第一件事是读取内核发送的启动引导消息。所有它从内核获得的句柄中,有一个
handle info entry
PA_HND(PA_VMO_BOOTDATA, 0)
。这是一个
VMO
包含来自
boot loader
的ZBI镜像。
userboot
从这个VMO中读取ZBI头部信息,查找第一个类型为
ZBI_TYPE_STORAGE_BOOTFS
的项。其包含
BOOTFS
镜像。此项的ZBI头部指示它是否被压缩,通常是这样。
userboot
映射VMO的这一部分。
userboot
包含LZ4格式支持代码,用于将此项解压缩到新的VMO中。
接下来,
userboot
检查它从内核接收到的环境变量,其表示内核命令行。如果存在字符串
userboot=
file
,随后
file
将作为第一个实际用户进程加载。如果不存在这样的项,则默认的
file
是
bin/bootsvc
。其包含在BOOTFS镜像中。
要加载文件,
userboot
实现了一个功能齐全的ELF程序加载器。通常加载的文件是一个动态链接的可执行文件,带有
PT_INTERP
程序头部。在本例中,
userboot
查找以
PT_INTERP
命名的文件并加载。
然后,
userboot
加载vDSO到一个随机的地址。使用标准约定启动一个新的进程,传递一个通道
channel
句柄和vDSO基址。在通道句柄上,
userboot
发送标准的
processargs
信息。它传递从内核接收到的所有重要句柄(需要替换特定的句柄,如进程本身和线程本身句柄,替换为新进程本身的,而不是
userboot
进程的)。
遵循标准程序加载协议,当
userboot
通过
PT_INTERP
加载程序,在主消息之前发送额外的
processargs
消息,旨在使用动态链接器。此消息包括一个
PA_LDSVC_LOADER
通道句柄,在此通道上,
userboot
提供一个最小实现的标准
loader service
.
userboot
只有一个线程,它处于循环中,处理加载器的服务请求,直到通道
channel
关闭。当它收到
LOADER_SVC_OP_LOAD_OBJECT
请求,它在BOOTFS中查找前缀为
lib/
的对象名文件,并返回其内容的VMO。因此,第一个"real"的用户进程可以(通常是)一个动态链接的可执行文件,其运行需要各种共享库。动态链接器、可执行文件和共享库都是从相同的BOOTFS页面加载,其后以文件的形式显示在
/boot
目录中。
将由
userboot
加载的可执行文件(i.e.
bootsvc
)通常应在启动完成之后关闭它的加载器服务通道。这让
userboot
知道它不再需要了。
当加载器服务通道关闭的时候(或者如果可执行文件没有
PT_INTERP
,因此不再需要加载器服务,进程已经启动完成),
userboot
不再有任何操作。
如果
userboot.shutdown
选项指定在内核命令行中
,
userboot
将等待它开启的进程退出,然后关闭系统(就像通过
dm shutdown
命令)。这对于运行单个测试程序,然后关闭计算机(或模拟器)很有用。例如,命令行
userboot=bin/core-tests userboot.shutdown
运行Zircon的核心测试程序,然后关闭。
否则,
userboot
不等待其启动的进程退出。
userboot
立即退出,留下第一个"真正的"用户进程负责处理系统其它部分的启动和关闭。
kernel/arch/arm64/start.S:323:IMAGE_ELF_ENTRY = _start
kernel/arch/x86/start.S:241:IMAGE_ELF_ENTRY = _start
Zircon
是 Google 新操作系统 Fuchsia 的
内核
,基于 LK - Little Kernel 演变而来。而 Little Kernel 前面一直作为 Android 系统的 Bootloader 的核心而存在。
Zircon
在此基础上增加了 MMU,System Call 等功能。
Zircon
目前支持 X86/X64 和 ARM 两种 CPU 平台,下面我将以 ARM6...
Magenta-
Userboot
在kernel初始化完毕后,需要跳转至user space并初始化user的init进程(devmgr),此也是user的第一个进程。为了可以顺利的
启动
user进程,magenta在 编译,初始化和
启动
阶段分别做了特殊处理。
userboot
就是专为此而实现的模块。包括:
kernel
userboot
:运行在kernel
空间
,为进入user
空间
做准备
user us
Kernel
Zircon
的
启动
:首先运行boot-shim.ld的ENTRY
(
_start
)
;//boot-shim.S的FUNCTION
(
_start
)
;//start.S汇编代码,之后到lk_main
(
)
;
~~//内调
zircon
/kernel/top/main.cc的lk_main
(
)
;
~~//创建”bootstrap2″线程,从init.cc的lk_init_level
(
)
,参数”global_prng_thread_safe”;//内调global_prng.cc的BecomeThreadSafe
(
)
;
~~//线程”bootstrap2″创建成功后执行bootstrap2
(
)
函
zircon
系统调用是有一个生成框架,类似于linux的SYSCALL_DEFINE宏。
系统调用号由系统调用在syscalls.abigen文件中的定义顺序决定,实际上是依次递增的。
在相关文件定义后,会自动申明zx_name的函数,
用户
之间使用的是这个zx_name的函数。
添加自定义系统调用
修改syscalls.abigen文件,增加自定义系统调用格式,包括系统调用名字,参数,返回值,格式...
Zircon
是一个基于对象的
内核
。
用户
模式代码几乎只通过对象句柄与操作系统资源交互。对象句柄可以被认为是与OS特定子系统的特定资源的活动会话。
Zircon
管理着如下的资源:
processor time
memory and address spaces
device-io memory
interrupts
signaling and waiting
应用程序的
内核
对象
Chan...
bootsvc是(通常)
用户
模式加载的第一个程序(相对于由
内核
加载的
userboot
。bootsvc提供几种系统服务:
包含
bootfs
内容的文件系统服务(/boot)
从bootf获取的加载程序服务
在准备好这些服务之后,它从
bootfs
启动
一个程序。这个程序可以使用[
内核
命令行参数](kernel-cmdline.md)- bootsvc.next指定(当前默认值为bin/devmgr)。...