1. 背景
1.1 目标
从零实现一款针对 onePlus 6/6T Android 手机操作系统。这是开篇,以后会定期更新进度,感兴趣的小伙伴,欢迎一起讨论!
我只是把这个项目当做练手,来达到理解的目的。我并不是要替代 Android AOSP,当然我也没这样的实力。
我们采用 Rust、内联 aarch64 ISA 汇编、参考 postmarketOS 源代码的方法,来一步一步实现。总之这个过程不会很轻松,要查阅很多资料。
1.2 预期的收获
最后,我希望通过这个项目,能有这些收获:
-
精通 Rust 语言,深刻理解一门系统级编程;
-
性能优化时,能考虑到机器的特点;
-
深刻理解软件如何控制硬件(SoC架构);
-
深刻理解 Android 手机的底层原理;
-
深刻理解 Android ASOP 的原理;
1.3 为什么从零实现
那为什么是从零实现?而不是看 Linux、Android AOSP 源代码,反而重复造轮子?
因为, 源代码阅读效率低,以下是具体原因:
-
庞大性: Linux 内核超过 3000 万行代码,AOSP 更甚。人脑无法同时处理如此巨大的信息量。
-
复杂性 (Complexity):
- 模块化与 耦合 : 内部高度模块化,但模块之间又存在复杂的依赖和交互。
- 抽象层次: 从汇编到 C 语言宏,从硬件寄存器操作到高级数据结构和算法,抽象层次众多。
- 并发与同步: 大量锁、信号量、原子操作等同步机制,理解多线程/多核环境下的代码流非常困难。
-
兼容性 ( Compatibility ):
- 架构兼容: 支持各种 CPU 架构(x86, ARM, MIPS, RISC-V 等),代码中充斥着
#ifdef和不同的实现。 - 设备兼容: 支持海量硬件设备,每个驱动都有不同的初始化和操作方式。
- 历史遗留: 许多代码是为了兼容旧硬件、旧标准或修复历史 Bug 而存在的,理解起来非常费劲。
- 架构兼容: 支持各种 CPU 架构(x86, ARM, MIPS, RISC-V 等),代码中充斥着
-
入口点与启动流程 (Entry Point & Boot Flow):
- 多级引导: Linux 内核的启动流程复杂,涉及汇编、C 语言的多个阶段,还会依赖 Bootloader 传递的参数。
- 平台差异: 不同 CPU 架构的启动入口和早期初始化代码完全不同。
-
设计哲学与约定 (Design Philosophy & Conventions):
- 大型项目有其独特的设计哲学、命名约定、编码风格和错误处理机制。这些如果没有人指导,很难快速领悟。
-
调试环境复杂: 许多底层代码只能在特定调试环境下(如 JTAG/SWD)才能有效调试,这本身就增加了学习难度。
-
无法理解意图:最重要的是你无法理解他们的代码为什么要这样写,这样写是在干什么?也就是无法理解意图,自然不知道它干了写什么。一行一行机器码(C代码、汇编代码)你都看得懂,但连起来就看不懂了,就更别谈理解底层。
从零开始玩具版的OS是理解底层最有效、最根本的方式, 它有以下优势:
-
控制复杂度: 只实现你需要的功能,代码量由你控制。
-
明确入口点: Rust
#[no_main]函数就是你的 OS 的唯一入口点,简单明了。 -
无兼容代码: 不需要考虑通用性、兼容性,只针对的目标 SoC(Xiaomi 9SE 骁龙712CPU) 编写代码。
-
逐步构建: 从点亮屏幕开始,一步步添加功能,而不是面对一个功能完整的黑盒。
-
亲手实践: 只有亲手编写每一个底层函数,才能真正理解内存分配、中断处理、驱动机制的工作原理。
-
错误暴露: 小系统中的错误更容易定位和理解,因为干扰因素少。
-
满足感: 每一步的成功都会带来巨大的满足感,激励你继续前进。
1.4 为什么是Rust?
我不想说 Rust 的什么,内存安全、零成本抽象、性能与C/C++一致。
从个人角度来说:我不会 C++, C 抽象层次太低。
从市场角度来说:我押注 Rust。
况且 Rust 已经作为 主线Linux 中的开发语言了。
1.5 为什么有这个想法?
无论是书籍还是互联网上,有关安卓手机操作系统的资料都很少,几乎没有,倒是基于PC端操作系统的资料非常非常多。
由于我很想使用机器码直接控制机器。于是我体验了一把:为 X86 CPU 编写一个玩具版操作系统,整个过程使用 x86 汇编编写。让我对“机器码控制机器”有了一定的认识,但我感觉还不够。再加上我有很多台二手安卓手机,于是我就想“既然我编写过pc端操作系统,那么也能为智能手机编写操作系统”,于是开启这条“不归路”。
1.6 为什么移动端操作系统资料少?
“为什么开发移动端操作系统的资料少,而开发 pc 端操作系统相关的资料那么多?” 我认为,这个问题非常重要
有以下几个原因
1. 问世时间
PC端操作系统的问世时间,更早,自然而然PC端的资料也就丰富了,但这不是主要原因。
2. 手机的特性
手机,不像PC那样很大,PC上各种 I/O 设备大多可替换。比如硬盘,不够了我再加,或者我换个SSD 固态硬盘;内存不够了,我再加个内存条;显卡不够用,我再换个高级的显卡。
手机,它是高度集成的,I/O 设备不可更换,所以它也没必要将 I/O 设备的接口,做的很标准、很公开。
由于手机设备的高度集成、高度闭源的特性,导致操作系统的开发变的很难。
因为硬件接口不是公开的,那么什么是硬件接口?就是CPU要控制一个I/O设备,必须要知道它的地址,即能找到这个设备;然后通过这个地址向它写入数据或读取数据,这个数据可以很复杂,也可以很简单。不知道数据的功能,就不能真正地控制 I/O 设备。
综上,这就是为什么:手机端的操作系统资料很少,因为手机设备高度集成,其内部的I/O设备不可替换,所以它的硬件接口没有标准化,没有公开。每一款手机的硬件接口都不同,很难形成向 PC 那样的高度资料共享。
3. 移动设备的产业链
下面用形象的比较,聊聊手机中的产业链,它是导致手机操作系统开发资料少的根本原因。
手机这门生意是:一层“包”一层,谁也不给看“配方”。
想象一下,造一部手机就像开一家超级汉堡店。
3.1 ARM 公司
- 是什么?提供核心肉饼配方 —— ARM公司
- 他们干啥? 他们不卖汉堡,也不做肉饼。他们只研究怎么让牛肉更好吃,然后把这个独家肉饼配方”(CPU 和 GPU 的设计图)卖给别人。
- 怎么赚钱? 谁想用他们的配方做肉饼,就得给他们一笔很贵的授权费” , 之后每卖出一个汉堡(CPU、GPU) , 还要再给 ARM 公司一点“提成”。
- 他们的底牌? 这个配方是 ARM 公司的专利,别人不能抄。而且全世界的厨师(软件开发者)都习惯了基于这个配方做菜,换配方太麻烦了。
3.2. 高通、联发科
-
是什么? 制作“特制肉饼”的中央厨房 (比如高通、联发科)
-
他们干啥? 他们是真正的大厨。他们花钱买了上面那家(ARM公司)的“独家肉饼配方”,然后往里加了更多自己的“秘制酱料” ,做成了一块功能超强的“特制肉P饼”。
-
什么是“秘制酱料”?
- 上网功能 (基带/Modem) : 怎么打电话、怎么用 5G 上网,这个技术是他们的看家本领。
- 拍照处理 (ISP) : 怎么让照片拍出来更好看、更清晰,算法是他们的秘密。
- GPS 定位、Wi-Fi 连接... 这些都是他们自己研发加进去的。
-
怎么赚钱? 他们把这块功能强大、包含了所有秘制酱料的“特制肉饼”(也就是 SoC 芯片),卖给开汉堡店的(比如小米、三星)。
-
为什么不公开“酱料配方”?
- 这是命根子! 他们家的汉堡比别人家的好吃,就靠这些酱料了。公开了,别人都学会了,他还怎么卖高价?
- 省事儿。 他们还会提供一套**“操作指南”(驱动和软件库)**,告诉汉堡店员怎么用这块肉饼。但这套指南只保证能用,具体肉饼怎么做的,一个字都不会提。这样他们就不用回答全世界美食家问的各种刁钻问题了。
- 安全和法规。 特别是上网打电话的功能,国家有严格规定,不能让别人随便改,不然会出大乱子。所以必须保密。
3. 3 手机制造商
-
是什么? 开连锁汉堡店的 (比如小米、三星)
-
他们干啥? 他们是最终的汉堡店老板 。 他们从中央厨房那里买来现成的“特制肉饼” , 然后自己设计面包、包装盒(手机外观) , 再配上自己选的生菜、番茄(屏幕、摄像头、等等I/O设备)。
-
怎么赚钱
- 卖汉堡(卖手机) :这是最直接的收入。
- 卖套餐里的可乐和薯条(互联网服务) :这才是大利润!他们在自己的店里(手机系统里),强制或者优先推荐顾客买自家的可乐(应用商店)、薯条(云服务)、甜筒(广告)。
-
为什么他们也不把“汉堡怎么组装的”方法公开? 、
-
商业竞争。 他们店的汉堡套餐搭配,是吸引顾客的秘诀。公开了,隔壁的竞争对手明天就学过去了。
-
控制顾客。 在自己的店里,当然是希望顾客只用自家的东西。系统不开放,顾客就更容易留在自己的生态里,持续消费。
-
总结
整个手机行业就是这么个玩法:
- ARM 公司 靠卖“配方”赚钱。
- 高通公司 靠卖加了“秘制酱料”的“半成品”(芯片和配套软件)赚钱。
- 小米公司 靠卖最终的“成品汉堡”(手机)和“套餐里的饮料薯条”(互联网服务)赚钱。
每一层都把自己的核心技术和赚钱的门道藏得严严实实的,只给下一层提供一个“能用就行”的接口(不公开)。
所以,作为一个想自己学做汉堡的“美食家”,跑到店里,最多只能拿到一张公开的菜单(GPL 的内核源码),但酱料配方(用户空间驱动库)和后厨操作流程(系统定制功能) ,老板是绝对不会给你的。这就是为什么在手机上搞操作系统开发,资料那么少,那么难。
1.7 为什么PC端不学习移动端 ?
为什么PC端不学习移动端,那样的产业链呢?商业模式呢?
1. 问世不一样
- PC 的诞生: 最早的 IBM 电脑,为了快点抢占市场,就把很多设计图给公开了。这就好比一个玩具厂,不仅卖玩具,还把模具图纸也公开了。结果,全世界的小作坊(其他公司)都开始照着图纸生产兼容的零件,比如显卡、声卡、硬盘。
- 结果: 从一开始,PC 就是个“攒出来的”东西。大家习惯了从 A 公司买主板,B 公司买显卡,C 公司买内存,自己组装。这个“散装”的习惯一旦形成,就改不掉了。
- 手机的诞生: 手机出来的时候,玩法已经变了。像苹果和高通这样的大公司,从一开始就是自己设计一整套方案。他们卖的不是零件,而是一个打包好的“核心套餐”(芯片+软件)。所以手机天生就是“整装”的。
2. 赚钱的方式不一样
-
PC 行业:
- 英特尔主要靠卖 CPU 赚钱。
- 微软主要靠卖 Windows 操作系统赚钱。
- 华硕、戴尔这些公司,主要是靠组装和卖整机赚钱。
- 他们是一个松散的联盟,谁也管不了谁。英特尔巴不得有更多的公司来生产兼容的主板和显卡,这样它的 CPU 才卖得更多。
-
手机行业:
- 高通卖的不是一颗简单的芯片,它卖的是一整套“解决方案”,包括芯片、驱动、各种闭源的软件库。手机厂商(比如小米)买了它的方案,就能省很多事,快速造出手机。
- 小米这样的手机厂商,除了卖手机硬件,很大一部分利润来自手机里的互联网服务,比如应用商店、云服务、广告。一个封闭的系统,更容易让用户使用自家的服务,钱才好赚。
3. 用户习惯和期望不一样
- PC 用户: 这么多年下来,玩电脑的人,特别是游戏玩家和专业人士,已经形成了一种“我的地盘我做主”的文化。电脑必须能自己换零件、能升级。如果哪个公司卖一台高端游戏电脑,说内存和显卡都焊死了不让换,那肯定会被骂得很惨。
- 手机用户: 大家买手机的时候,就把它当成一个整体的消费品,像电视、冰箱一样。没人会想着去给手机换个 CPU 或者加根内存条。用户关心的是手机好不好看,用起来流不流畅,拍照好不好。
4. 他们试过变“封闭”吗?
当然试过
- 别以为 PC 厂商不想学手机那套。其实他们一直在努力。
- 苹果的 Mac 电脑就是最好的例子。特别是换了自家的 M 系列芯片后,CPU、GPU、内存全都焊在一起,操作系统也是自家的,越来越像一个大号的 iPhone,非常封闭。
- NVIDIA 的 CUDA 也是。它搞了一套自己的编程平台,专门用于科学计算和 AI,你用了就离不开它的显卡。
- 微软也搞过只能从应用商店装软件的 Windows 版本。
但是,因为 PC 市场“散装”的历史太久了,用户也习惯了自由开放,所以这些“封闭”的尝试在整个 PC 市场里,还没有成为绝对的主流。而在手机市场,一出生就是“整装”和“封闭”的玩法。
2. 面临什么问题?
我们的目标是实现:移动端操作系统。
为了实现此目标,我们必须依次解决一系列根本性的问题。这些问题涵盖了从硬件的唤醒,到程序的执行,再到与用户和外部世界的交互。
2.1 如何让我们的代码在手机上执行?
这是所有问题的前提。一块没有软件的手机,就是一块无法响应的硬件,就是废铁。
- 启动链: CPU 的第一条指令从何而来?我们如何将控制权从厂商固化的 Bootloader 手中,交接到我们自己的代码里?
- 硬件初始化: 在我们的代码开始执行时,硬件处于何种状态?我们如何与最基础的硬件(如串口)进行第一次“对话”,以确认我们“活了”?
2.2 如何多任务?
一个现代操作系统必须能同时处理多个任务,即使它只有一个 CPU 核心。
-
并行:如何利用多核CPU,让不同的线程处于不同的 Core CPU 中?
-
并发:如何在多个任务之间进行切换,让它们交替使用 CPU?这需要什么机制来保存和恢复每个任务的现场(上下文)。
-
调度:切换的“时机”和“策略”是什么?我们如何保证公平性,防止某个任务霸占 CPU?(这就引出了对定时器中断的需求)。
2.3 如何独占内存?
程序不能直接在混乱的物理内存中“裸奔”,它们需要一个受保护的、私有的工作环境。
-
隔离与翻译: 如何防止一个程序窥探或破坏另一个程序的内存?我们如何将程序看到的“虚拟地址”转换成真实的“物理地址”?(这就引出了对 MMU 和页表的需求)。
-
内存管理: 当一个新任务需要内存时,我们如何从空闲的物理内存中,安全地分配一块给它?用完后如何回收?
2.4 如何控制 I/O 设备
操作系统必须能够驾驭手机上形形色色的硬件,才能实现有意义的功能。
- 简单 I/O:如何控制那些接口简单、行为直接的设备,如 LED 灯 (GPIO) 、物理按键 (GPIO+中断) ?
- 持久化存储:如何与存储芯片 (UFS/eMMC) 通信,以实现数据的永久保存?这需要理解什么样的协议和硬件机制(DMA)?
- 复杂协处理器:对于像 GPU、Wi-Fi、GPS 这样自身就是一台复杂计算机的“黑盒”设备,我们如何理解并实现与它们对话的、高度复杂的协议?
2.5 系统调用与用户态问题?
内核提供了强大的能力,但必须有一个安全、受控的方式,让普通的用户程序能够请求这些服务。
- 特权级切换:如何让程序从低权限的“用户态”安全地陷入到高权限的“内核态”来请求服务,并在服务完成后安全返回?
- 接口设计:我们应该向用户程序提供哪些核心的系统调用接口?(如 read, write, fork, exec)
2.4 总结
这些问题,其实和开发 PC 操作系统所面临的问题一致。但具体的实现细节差异较大,因为设备形态很不同。
3. 移动设备的启动流程
未完 ....... 待续