Fuchsia入门-zircon微内核单独编译启动

原创 thatway 那路谈OS与SoC嵌入式软件 2023-03-07 18:06

“**实践出真知**”,对新手来说,别人给你说代码架构是怎样怎样的,给你一堆文档,看完还是**一头雾水**。实践很简单,就是代码编译运行,打log。之前的文章:[Fuchsia入门-简介和代码介绍](http://mp.weixin.qq.com/s?__biz=MzUzMDMwNTg2Nw==&mid=2247484294&idx=1&sn=f95790f9d109f8653974fd2ca0a8dbc6&chksm=fa5285a2cd250cb45f99a71e3d58539ea55c4090204c909fc0a12ae260ed15563bd437b12ef5&scene=21#wechat_redirect)  

,介绍Fuchsia的代码编译运行,但是操作起来很不爽

  • 下载代码需要几个小时

  • 代码下载编译后达到将近100G空间

  • 编译需要两个小时

  • qemu运行卡顿

这还怎么**学习调试**  

,官网就是给咱摆了一道
,然后官网基本就是纯概念的讲解。针对这种情况,还想学习Fuchsia主要是zircon微内核原理的人就很痛苦,要么直接看网页代码
https://cs.opensource.google/fuchsia/fuchsia/+/main:zircon/;bpv=0;bpt=0

然后看官网文档

https://fuchsia.dev/fuchsia-src/get-started?hl=en上看看纯概念文档。

但是还是那句话,**不运行这能学吗?**这也是本篇文章要解决的问题,我们需要**小而精**而且可以qemu运行的代码,直接上干货:**https://github.com/PanQL/zircon**  

1. Zircon内核编译运行

https://github.com/PanQL/zircon这个是清华的一个同学从Fuchsia官方的20190715版本代码仓库中分离出来的**Zircon代码**  

,用于进行Zircon内核学习,现在已经没有了。可以通过Makefile脚本进行编译并在qemu上运行

Zircon   

是为
Fuchsia
提供支持的核心平台

Zircon
由内核(源代码在
/zircon/kernel
)以及系统启动、与硬件对话、加载用户空间所必需的一小组用户空间服务、驱动程序和库(源代码在
/zircon/system/

)组成处理并运行它们等。
Fuchsia
在此基础之上构建了一个更大的操作系统。

可以这么说,学习  

Fuchsia

精华和原理
,搞清楚这个
Zircon
就差不多了。

编译环境搭建:

为了减小仓库体积,将prebuilt目录进行了大幅调整;因此运行之前请下载google预编译好的**clang**  

https://chrome-infra-packages.appspot.com/p/fuchsia/clang/linux-amd64/+/

解压后放到某个权限合适的位置,这里我们放入/home/book/tools/clang

然后在代码的public/gn/toolchain/clang.gni
绝对目录
修改为对应位置,如下:

默认x64
架构编译运行
,在代码根目录下执行:

Make build
Make run

如果想在arm平台
上执行,需要修改out/args.gn中的legacy-image-x64为legacy-image-arm64,然后执行:

make build
make runarm

2. 学习感想分享


现在我们有**可运行代码**  


文档
,就像
OS
入门系列里面各个
OS
都有这两个东西,但是直接看代码也看不懂,直接看文档也不行,然后入门很困难,这里有一个大的误区:

面对一个复杂系统,代码和文档**只看一遍是肯定不行的**  

,需要三遍起步,而且要代码文档循环的一遍一遍看。

事实就是这么残酷,没有不劳而获,只能付出时间。万事开头难,还有一个关键就是**怎么开头**  

,我感觉开头首先要调动兴趣和积极性
,从自己擅长的角度,比如我觉得这个代码可以运行能打印出来我写的log
,我就会相信这个代码,感觉有兴趣去探索,那我们开始:

我们先抛开官网那一堆概念,**直接看代码的启动过程**  

,遇到不懂的代码再去看官网概念文档。然后接触一部分概念后,再整体去看一般概念(记不全),然后就是继续看代码,看代码有瓶颈了再看一般文档,循环往复,直到代码和文档都懂了。

修改代码:

运行:

到这里,是否能提起你的兴趣
去探索这个代码?

假如你已经有兴趣探索这个代码,那么接下来按照上面说的代码+文档循环往复,这里有一个**功力**  

的问题:
有的人看的快,有的人看的慢
。或许你会说这跟
工作经验
有关系,大佬看一天的进度,小白可能需要一星期还得加班。是什么造成这样的差异?我们先扯点我自己的见解再开始进入代码,功力的高低的表现,比如有人说看代码如喝水,千把行不歇的,有让你说每一行看着都困难,一个函数摸索老半天,我感觉这里的功力
共性知识
的积累相关,看的多了,积累的基础共性知识就多了,就看的更快。那么共性知识的本质是什么,还是人类理解世界的能力
,纵观人类的组织结构例如一个王朝
的组织就跟操作系统
里面很多机制很像,再本质一点为什么人类会这么组织,那就是我们的基因
,基因又是从自然
而来,假如换一个星球一切的根基就又不同了,像三体游戏里面从文明
的角度来看了,人类是有局限性,但是这种局限性促使我们更快的了解人类的共识,发展自我。

我们去理解一个新的事物可以借助**共识**  

,但是不能拘泥于细节和条条框框,站在共识的角度更容易去创新,例如最近马斯克说的汽车这么多零部件,当然这些零部件都是有用的,但是为什么不一体浇筑
岂不是更简单有效率,这种野蛮而又简单的想法像狼入羊群
一样,冲击传统思维但是又符合基本规律

有时自己也有一些疑问,比如**学习这些东西对自己有多大帮助**  

,以后忘记了怎么办,这点还想需要一个大局观,把握本质
脉络可能就是学习的真意,就像领导不看你细节的代码描述只要一个流程图或者框架图,随着这些知识的理解融汇
,最后达到一个复杂的、不可替代的程度,算是一个人才
了。

路漫漫,笔者也是一个技术菜鸟,啥都不精通,毅力也不够,天赋也没有,写的这些文章也都是入门篇,之后代码分析估计你会体会到**一顿乱拳**  

的感觉。只是去想想这些,总有点帮助。下面进入代码的的世界。

3. 编译过程分析

编译就是执行make build
,在代码根目录Makefile
中:

build: config
cd out && ../buildtools/ninja
config:
buildtools/gn gen --export-compile-commands=x64 out

build依赖于config,conffig用了gn命令
,然后build中又使用了ninja命令
就完成了编译

代码doc文档里面有这两个工具的解释,都是谷歌很好用的编译工具。不细研究,我们先知道gn就是生成需要编译那些文件到out/compile_commands.json,ninja就去编译这些文件成二进制,ninja的优势就是增量编译和并行编译,代码修改后二次编译非常的快,就是这个痛点谷歌大牛
搞了这个工具,用着
超级棒

运行代码执行:make run

./scripts/run-zircon-x64 -z
./out/legacy-image-x64.zbi -t ./out/multiboot.bin

./scripts/run-zircon-x64是个脚本:

exec $DIR/run-zircon -a x64 "$@"

exec $DIR/run-zircon也是个脚本:

exec $QEMU -kernel "$QEMU_KERNEL"
-initrd "$QEMU_INITRD" \
        $ARGS -append "$CMDLINE"
"$@"
  • $QEMU_KERNEL就是./out/legacy-image-x64.zbi,也就是

kernel镜像

  • $QEMU_INITRD就是./out/multiboot.bin

  • $ARGS就是

qemu命令

的参数

  • $CMDLINE就是传给

内核的参数

,在内核代码里面可以获取这些信息

那么./out/multiboot.bin是个什么东西?

Multiboot
是一个协议
,一般协议就是一个约定为了对接两部分的执行实体,具体来说BootLoader去适配Kernel,一般来说是一对一的关系,那么某个bootloader就不具有通用性,有问题就可以通过抽象一层来解决,所以就有了Multiboot协议,满足这个协议的bootloader和kernel可以随意的适配
。启动的顺序为qemu->bootloader->multiboot->kernel

Bootloader和multiboot的代码在zicron工程下有,这里不深入研究,直接看kernel。  

4. kernel入口分析


Kernel的入口为_start
,代码在kernel/arch/x86/start.S

从进入_start开始,到跳转到C语言入口lk_main
之前,start.S中主要依次完成以下这些任务:

  • 设置临时栈

  • 清空bss段(此处涉及KASLR的实现,清空之前进行了复制)

  • 初始化boot时期的alloc机制

  • 设置新的页表

  • 跳转到fixups代码所在位置,修正KASLR带来的偏差

  • 设置中断向量表

平台初始化可以参考:https://blog.csdn.net/ganyao939543405/article/details/85204534

进入lk_main()函数就是大家熟系的c++语言
了,这里我们之前也加了打印。

为什么kernel的入口是_start?

legacy-image-x64.zbi可见kernel的格式是zbi(Zircon Boot Image),参考:https://fuchsia.dev/fuchsia-src/concepts/process/everything_between_power_on_and_your_component

ZBI
格式
是一种简单的容器格式
,它嵌入了引导加载程序传递的项目,包括特定于硬件的信息、提供引导选项的
内核

命令行


RAM
磁盘映像(通常是压缩的)。
内核在引导的早期阶段提取一些基本信息供自己使用。

kernel/target/pc/multiboot/multiboot-main.c中对zbi进行了解析

// Find the kernel item.
const zbi_header_t* kernel_item_header = NULL;
result = zbi_for_each(zbi, &find_kernel_item,
&kernel_item_header);
if (result != ZBI_RESULT_INCOMPLETE_KERNEL)
{
  panic("ZBI missing kernel");
}
// This is the kernel item's payload, but it expects the whole
// zircon_kernel_t (i.e. starting with the container header) to be loaded
// at PHYS_LOAD_ADDRESS.
const zbi_kernel_t* kernel_header = (const void*)(kernel_item_header + 1);

zbi是二进制文件,里面开头放了zbi_header_t
zbi_kernel_t

这两个结构体的定义见system/public/zircon/boot/image.h,这个文件也是ZBI格式定义的核心文件。

Multiboot解析zbi文件后把kernel入口地址放入了寄存器,之后就跳转执行了

"a"(kernel_entry), // %rax: kernel entry point

再来看_start,在kernel/arch/x86/start.S中

// These symbols are used by image.S
.global IMAGE_ELF_ENTRY
IMAGE_ELF_ENTRY = _start

在kernel/image.ld链接文件中,规定了入口地址

ENTRY(IMAGE_ELF_ENTRY)

但是看这个链接文件上面的解释,zicrcon并没有用到这个ENTRY而是对zbi二进制文件直接解析找到的IMAGE_ELF_ENTRY,同样在kernel/arch/x86/start.S中如下:

// ZBI file header (zbi_header_t)
ZBI_CONTAINER_HEADER(_zbi_file_header,
boot_load_end - _zbi_kernel_header)
// ZBI kernel header (zbi_header_t)
DATA(_zbi_kernel_header)
.int ZBI_TYPE_KERNEL_X64
.int boot_load_end - _zbi_kernel_payload
.int 0
.int ZBI_FLAG_VERSION
.int 0
.int 0
.int ZBI_ITEM_MAGIC
.int ZBI_ITEM_NO_CRC32
END_DATA(_zbi_kernel_header)
// ZBI_TYPE_KERNEL payload (zbi_kernel_t)
DATA(_zbi_kernel_payload)
.quad PHYS(IMAGE_ELF_ENTRY)     //该处地址为start.S中的_start地址
.quad boot_bss_end - boot_load_end // 在该image加载之后,还应该余下多少内存空间
END_DATA(_zbi_kernel_payload)
篇幅有限,下次继续从**lk_main**  

介绍启动流程
,可能会更多的涉及zicron内核里面的内核对象
,这些内核对象是OS里面很基本和简单的抽象,不像linux宏内核那么复杂,更容易理解OS。

后记:

Fuchsia入门文章:[Fuchsia入门-简介和代码介绍](http://mp.weixin.qq.com/s?__biz=MzUzMDMwNTg2Nw==&mid=2247484294&idx=1&sn=f95790f9d109f8653974fd2ca0a8dbc6&chksm=fa5285a2cd250cb45f99a71e3d58539ea55c4090204c909fc0a12ae260ed15563bd437b12ef5&scene=21#wechat_redirect)  

阅读量
目前还是最高
的,说明大家对这个系统很有兴趣,一起来学习吧。

啥都懂一点
啥都不精通

干啥都能干
干啥啥不是

专业入门劝退
堪称程序员杂家
”。

后续会继续更新,  

纯干货
分析,无广告,不打赏,欢迎
分享给朋友
,欢迎
评论交流

results matching ""

    No results matching ""