【小白向】Android内核折腾计划(一)——起步

【小白向】Android内核折腾计划(一)——起步

##0 说在前面

提醒

本系列文章以非 C 语言开发者的角度编写,且假设你已经会内核编译的基本流程,后续内容随缘更新。本文当前为第 3 版,更新于 2025-05-10。

##0.5 战斗准备

开始战斗前的物质准备:

硬件:一台多核性能不是太差的电脑

Linux 环境:单独安装或 WSL 均可

Clang 编译器:推荐最新 Neutron Clang,ZyC Clang 21 会导致小米系统无法正常显示内核版本,故建议避雷

AOSP GCC 交叉编译器:默认最新(在这里找最新标签,标签格式为 “android-xx.x_rxx”)

git 相关使用方法(多分支管理、rebase、cherry-pick 等)

以及 3 个小提示!

为了方便后期进行 cherry pick 等操作,强烈建议在条件允许的情况下,以 git clone 的方式将内核源码复制到本地,而非通过下载内核源码的压缩包。

即使源码只能以压缩包形式获取,也建议通过 git init 命令将其初始化为一个 git 仓库。

为了方便解决冲突并兼顾轻量性,推荐使用 VSCode。

当然,最重要的,是一颗沉静得下去的耐心。

##1 寻找可用的内核源码

此处 “可用” 指编译过程中不会因为源码的 bug 而中止,且编译后的内核可以正常开机。

#确定哪些源码能用

首先我们打开手机设置,在 “关于本机” 中确认内核的大版本号 ——Linux 的版本号(即数字部分)为 x.y.z 结构,但它并不遵守语义化规则。其中 x.y 在本系列文章中规定为 “大版本号” 或 “主线版本号”,z 规定为 “小版本号” 或 “基线版本号”。

举个栗子,有个手机打开系统设置后查询到内核版本如下:

则可以确定其大版本号为 5.15。

大版本号 ≥ 5.10 的内核为支持 GKI 2.0 的内核,以下为方便简称 GKI 内核;大版本号 < 5.10 的内核不支持 GKI 或仅支持 GKI 1.0,以下简称非 GKI 内核。

首先,不管是什么情况下,首选手机厂商提供的内核源码。

小米 & 红米:MiCode/Xiaomi_Kernel_OpenSource (GitHub)

一加:OnePlusOSS (GitHub)

oppo:oppo-source (Github)

真我:realme-kernel-opensource (GitHub)

华为:Huawei Open Source Release Center

vivo:vivo Open Source

荣耀:opensource - HONOR

中兴 ZTE:Opensource

但是多数厂商在开源时移除或修改了部分源码,使编译无法顺利完成,或编译后无法引导系统(OPPO 及子品牌系列将 dtbo 换为官方的即可开机)。考虑到本文读者应该不具备修复相关 bug 的能力,此处就需要另寻仓库。对于大版本为 5.10 及以上的高通机型,可以直接选择来自高通的源码。

5.10:clo/la/kernel/msm-5.10 - CodeLinaro

5.15:clo/la/kernel/msm-5.15 - CodeLinaro

6.1 及以上:clo/la/kernel/qcom - CodeLinaro

(注:高通的内核源码是以 tag(即标签)的形式发布的,命名规则请参考 #6)

对于其它 GKI 设备,也可以使用谷歌官方的通用源码,不过问题就是如此会失去 SoC 厂商和手机厂商的优化。

谷歌通用内核:kernel/common - Git at Google

当然,还有一个全版本通用的方法——在 GitHub 上找寻设备的第三方源码。不过这样找到的源码仓库,其性能和续航表现全部依赖于维护者本人的技术水平。

第三方内核的搜索关键词是:kernel_<手机厂商名>_<设备代号>/。比如搭载骁龙 8 Gen 2 的小米 13 Pro,手机的代号为 nuwa,SoC 的代号为 sm8550,就需要搜索 kernel_xiaomi_nuwa 或 kernel_xiaomi_sm8550。

提醒

对于非 GKI 设备的第三方内核,这些内核中有的只能供类原生(即 OSS vendor 的系统)使用,有的只能供官方系统(即官方 vendor 的系统)使用,一般会标注在项目简介内,可根据自己需要去选择。

#开始编译!

接下来就是内核编译的过程,网上教程很多,这里暂不赘述。看到控制台输出这个,就说明编译成功~

... ...

OBJCOPY arch/arm64/boot/vmlinux.bin

OBJCOPY arch/arm64/boot/Image

GZIP arch/arm64/boot/Image.gz

Kernel: arch/arm64/boot/Image.gz is ready (#1)

至于如何刷入内核,可以参考:[基础教程 2] 如何使用 AnyKernel 3 打包内核

##2 了解 commit 规范

请注意,对我们而言,规范的 commit 信息的最主要的意义在于让我们在后期的优化时读懂别人在干什么 ,其次同时也要让别人读懂我们在干嘛。

安卓内核既然也是 Linux 内核的一种,那么其 commit 规范也会受到约束。(虽然你不遵守规范也没人追究)一般情况下,一条标准的 Linux 内核的 Git commit 消息应当如下所示。

[scope]: [subject]

[description]

(可选,对于个人项目,确实写不出来或没必要可以不写

但如果要贡献给Linux或AOSP上游,则要越详尽越好)

[signature]

比如 torvalds/linux@3cc4e13

这样的 commit 就很标准。scope 是 cgroup;subject 是 “Fix potential … max_depth”,概括此 commit 主要作用;中间 description 内容是详细介绍这个 commit 做了啥;最后是 signature,所有参与了这个 commit 的人进行署名。

#scope

此处 scope 即作用域,表示修改的范围,如某个特定的功能模块。若无法确定,则一般情况下取作出的修改所在的目录即可,且一般不超过两层。如上图的 commit 仅修改了 cgroup 的控制组,则 scope 为 “cgroup”。

如果一个 commit 修改涉及到了多个模块的改动,即有多个 scope,则需要用英文逗号隔开,如 torvalds/linux@c6f53ed:

mm, memcg: cg2 memory{.swap,}.peak write handlers

如果修改的是编译配置(保存在 arch/<架构>/configs 目录),则 scope 应为修改的对应 defconfig,如 hfdem/android_gki_kernel_5.15_common@cbf9873 的 scope 就是 “gki_defconfig”。

gki_defconfig: Set ssg as default I/O scheduler

如果修改覆盖的范围较广,或修改内容涉及很多方面,则 scope 应为 “treewide”,如 kernel_common@dab2144。

treewide: correct the typo 'retun'

如果该 commit 是从上游(即主线或更高版本的基线)移植下来的,即反向移植(backport),则 scope 前应加上 “UPSTREAM: ”,如 kernel_common@0c24e4d。

UPSTREAM: crypto: xts - use 'spawn' for underlying single-block cipher

如果该 commit 的内容来源于非 Linux/AOSP 官方的仓库,则 scope 前应加上 “FROMGIT: ”,如 kernel_common@489cecd:

FROMGIT: binder: refactor binder_delete_free_buffer()

#signature

由于我们只是进行个人项目的开发,故 signature 部分仅保留署名(即 sign-off)部分即可。在 VSCode 的设置中搜索 “sign off”,打开 “Always Sign Off” 即可在每次提交时自动署名。

如果该 commit 还有他人参与,别忘了在最末尾加上个 Co-authored-by。

##3 自定义内核本地版本号

既然知道了 commit 规范,那么我们先来改改本地版本号练练手吧!

本地版本号说白了就是给内核版本加后缀,使其具有辨识度,这也是很多第三方安卓内核所会做的。咱可以从手机设置里查阅,或和 Linux 发行版一样通过在终端输入 uname 命令查询内核版本:

u0_a240@localhost ~ > uname -r

5.15.148-android13-8-00004-ge488687c12ef-ab11838684

在这个例子中,“android13-8-00004-ge488687c12ef-ab11838684” 这个后缀就是内核的本地版本。

而这个后缀可以通过 .config 或 defconfig(文件位置见 scope 处)中的 1 个进行修改,它们都是编译的配置文件。像编译时间或内核版次等经常变动的字串,建议在.config 文件中修改,以防止原 defconfig 被 git 追踪带来的麻烦;而像内核名称之类的固定内容则建议在 defconfig 中修改。

注:此处 .config 仅指在编译前执行 make xxx_defconfig 后在根目录产生的.config 配置文件。

直接查找文件中的 CONFIG_LOCALVERSION 条目,有就修改,没有就加上,记得值用英语的双引号包起来就行。

# defconfig 部分配置

CONFIG_LOCALVERSION="-Rudeus"

# 修改并直接编译后内核运行uname -r命令结果

5.15.148-Rudeus-android13-8-00039-g36ed888b10-dirty

为毛我们自定义的 “-Rudeus” 本地版本后边还有一串呢?在 CONFIG_LOCALVERSION 配置下边还有一个 CONFIG_LOCALVERSION_AUTO,这个条目开启后可以在编译开始时检测当前版本控制工具(如 git、svn 等),通过它追踪编译时的版本(即该 commit 所对应的独一无二的 hash 值)并将版本写入到内核版本中。如果你的修改没有提交给 Git,还会再加上一个 “-dirty” 后缀。喜欢就加上,不喜欢就关掉,例如我使用以下 defconfig 配置:

# 此处关掉了默认打开的CONFIG_LOCALVERSION_AUTO

CONFIG_LOCALVERSION="-Rudeus"

# CONFIG_LOCALVERSION_AUTO is not set

# uname -r 命令运行结果

5.15.148-Rudeus

接下来,提交你对 defconfig 的修改。

git commit -m "defconfig: Rename kernel to Rudeus" --signoff

大功告成!恭喜你已经自己做出了第一个 commit!

##4 集成 KernelSU

为方便演示,此处以官方分支的 KernelSU 举例,其它分支同理。部分 KernelSU 分支可能会有其它要求,以其文档为准。

KernelSU 允许我们通过 KPROBES 以驱动的形式集成进内核中,所以需要事先在 defconfig 中启用它。

# defconfig 部分配置

# 如果已经有了的配置项请勿再写一次

CONFIG_KPROBES=y

CONFIG_HAVE_KPROBES=y

CONFIG_KPROBE_EVENTS=y

为了方便后期更新,以及托管平台的管理,我们不采用 KernelSU 提供的一键集成脚本,而是直接 clone 到本地。进入 drivers/staging 目录(要在外层也行,看你心情)克隆 KernelSU。

cd drivers/staging

git clone https://github.com/tiann/KernelSU kernelsu

并将 KernelSU 设为本内核项目的子模块,在根目录创建 .gitmodules,添加:

[submodule "drivers/staging/kernelsu"]

path = drivers/staging/kernelsu

url = https://github.com/tiann/KernelSU.git

接下来分别修改 drivers/staging 目录下的 Makefile 和 Kconfig,添加 KernelSU 条目即可。(绿色代码为需要在对应文件中添加的内容)

diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig

index cc9d9fa0e1e38..ecfd48f4fa93 100644

--- a/drivers/staging/Kconfig

+++ b/drivers/staging/Kconfig

@@ -102,4 +102,6 @@

source "drivers/staging/wfx/Kconfig"

+source "drivers/staging/kernelsu/kernel/Kconfig"

+

endif # STAGING

diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile

index cc9d9fa0e1e38..ecfd48f4fa93 100644

--- a/drivers/staging/Makefile

+++ b/drivers/staging/Makefile

@@ -41,3 +41,4 @@

obj-$(CONFIG_QLGE) += qlge/

obj-$(CONFIG_WFX) += wfx/

+obj-y += kernelsu/kernel/

最后,提交 commit,在编译时选择启用 KernelSU,大功告成!

##5 合并 AOSP 上游代码

绝大多数情况下,同一个设备的 Android 内核因驱动迁移难度大,只能局限在一个 LTS 大版本内,所以我们通常需要将该大版本下对应的 Linux 上游合并进来,以使环境更安全、并获取更新的从新版内核特性。这就是 “更新基线” 或 “合并上游”。

绝大多数情况下,只要保证大版本一致,进行合并后几乎不会遇到无法引导的情况。

首先添加 AOSP 的内核源码仓库,并且拉取你内核所在大版本(非 GKI)或 KMI 版本(GKI,KMI 版本是什么?)的对应的基线分支。以下是列出一些基线分支的名称。

4.4:deprecated/android-4.4-p

4.9:deprecated/android-4.9-q

4.14:deprecated/android-4.14-stable

4.19:deprecated/android-4.19-stable

5.4+:androidxx-y.z-lts(此处星号需要与你手机 GKI 内核的 KMI 版本匹配)

# 由于谷歌代码库在大陆访问不便,此处使用清华大学TUNA的AOSP镜像源

git remote add aosp https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/kernel/common.git

# 假设手机的内核是5.15.148-android13-8-...

# 那么需要拉取的分支是android13-5.15-lts

git pull aosp android13-5.15-lts

然后以 AOSP 的分支为基础,切换到一个新的分支,我自己一般命名为 base-aosp,表示该分支的内容就是 AOSP 的基底 / 基线。

git switch aosp/android13-5.15-lts

git switch -c base-aosp

现在,AOSP 基线的分支就处理好了。切回到原分支并合并它!

# 假设原分支名为dev

git switch dev

git merge aosp/android13-5.15-lts

如果合并时出现冲突,建议通过 VSCode 解决冲突,一般情况下全部选择传入。合并时的信息无需遵循 commit 规范,只用默认的 “Merge ‘xxx’ into xxx” 即可。

##6 合并 SoC 厂商上游

由于其它主流移动设备 SoC 厂商不直接开源或直接不开源上游,此处只讨论高通的情况。

这是一个可选项,但合并 SoC 厂商的内核源码也能让内核的驱动得到更新、调度得到优化。最重要的是,通过更新的设备树和 bpf 等,能使内核支持的 Android 版本得到提升。

#确认源码标签

提醒

文中提到的标签具体数据可能因时间而变动,此处仅供参考,请以实际情况为准。

由于高通每次将发布源码发布至内核仓库的特定标签中,故需找到对应标签后再 pull 并 merge。

对于非 GKI 内核,其 tag 命名格式为 LA.UM.x.y.cz-abc00-平台名称 .0(粗体部分为固定内容),x.y 为平台版本号,z 和 abc 也是版本号但意义不明,一般来说版本号越大,内核就越新,内置驱动越新,优化越好,且不同版本号越靠前优先级越高。

由于非 GKI 设备的内核不通用,故需要注意选择的 tag 所适用的平台是否与自己的设备匹配,如平台为 SMx150 的 tag 编译出来的内核只能在骁龙 855(SM8150)或骁龙 675(SM6150)上运行。

有遇到带 QSSI 后缀的 tag,可暂时浅显理解为最高支持的安卓版本。

对于 GKI 内核,其 tag 命名格式为 KERNEL.PLATFORM.x.y.rz-0ab00-kernel.0,版本规则同上。但需要注意:x 不同,适用的平台和内核大版本均会不同,但该大版本下所有 SoC 都可通用这套源码。

x=1 → 大版本号 5.10,平台 SMx450

x=2 → 大版本号 5.15,平台 SMx550

x=3 → 大版本号 6.1,平台 SMx650

实操步骤

首先将高通的内核源码拉取到本地并合并,这里以驱动骁龙 8Gen2 的 5.15 举例。

# 添加高通的5.15通用内核仓库

git remote add qc https://git.codelinaro.org/clo/la/kernel/msm-5.15.git

# 拉取高通代码到本地

git pull qc KERNEL.PLATFORM.2.0.r35-01100-kernel.0

git merge qc/KERNEL.PLATFORM.2.0.r35-01100-kernel.0

一般情况下,只要合并 AOSP 上游完成后,再合并高通上游,是不会出现冲突问题的,可放心食用。

##7 如何简略地优化内核?

由于专业知识的欠缺,所以我暂且把 “性能优化” 拎成几个小的方面:

简易的配置项修改(这也是我们为数不多能够自己参与的优化项目)

“援引” 他人的优化类目

#手动调整部分简单配置项目

#ZRAM

简而言之,ZRAM 技术是通过压缩算法(一般是 LZ4)将内存中指定的数据进行压缩,其实质是以牺牲 CPU 性能来换取内存。

ZRAM 的配置及实现源码在 drivers/block/zram/zram_drv.c,然而对于基础知识尚且不足的我们,能够修改的只有 ZRAM 的大小。但是请注意,ZRAM 的大小不要超过实际内存大小,否则可能会导致意外发生(逃

ZRAM 大小的数据通常在上述文件中 if (!disksize) 的上一行。

@@ -1725,7 +1725,7 @@

int err;

-disksize = memparse(buf, NULL);

+disksize = (u64)6144 * SZ_1M;

if (!disksize)

return -EINVAL;

像这样就是将 ZRAM 固定为 6GB。

#Swappiness

swap 即交换分区,它是手机存储上开辟的一小块空间,在内存使用量达到一定值时临时充当内存的作用。而 swappiness 就是调整 swap 分区启用阈值的参数,则可以在 mm/vmscan.c 中通过 vm_swappiness 进行修改。

@@ -191,7 +191,7 @@

* From 0 .. 200. Higher means more swappy.

*/

-int vm_swappiness = 60;

+int vm_swappiness = 160;

static void;

#“援引” 他人优化类目

优化的核心是借鉴。——鲁迅根本没说过这句话

毕竟专业知识不足,使用他人造的轮子并非可耻之事。那么我们应如何使用他人的优化项目呢?

这就要提到我们后续进行绝大多数优化工作的工具——cherry pick——它能从其它分支中 pick 出你设定的 commit 到你所在分支上,我们可以通过它来拣选他人的优化 commit 并应用到自己的内核上。

首先将他人的内核仓库设置为远程仓库,pull 下来即可

# 假设存在一个第三方内核项目,主开发分支为dev-14,优化commit从129ffd0到ec6caa5

git remote add miracle https://github.com/Project-Miracle/android_kernel_xiaomi_raphael

git pull miracle/dev-14

git cherry-pick 129ffd0...ec6caa5 -x

此时我们就可以看到,我们自己当前所在的分支上多出了几个 commit,它们就是从另外那个分支上 cherry pick 下来的。

替换或更新原内核的部分功能

提醒

注:此 “模块” 指实现特定功能的一系列单一功能的代码,并非 “Linux 内核模块”。

就拿经常被第三方内核拿来做 ZRAM 压缩的 zstd 举个栗子吧,它存放在 lib/zstd 目录下,而且绝大多数情况下不受 Linux 版本的影响。首先找一个 zstd 更新了的内核仓库,比如 ferstar/xiaomi_xaga_kernel,先 pull 下来,同时再打开对应的目录的 commit 历史记录,只要不是 AOSP 或 Linux 官方的 commit,均为优化或更新项。

按照 cherry pick 的方法一个一个 pick 就行,更新功能就这么简单。理论上来说,现在你的 zstd 肯肯定定能够正常编译成功了!其它相关的库大致也是这个样子。

#更新内核部分驱动

高通将内核源码开源到 CodeLinaro 的同时,也开源了配套的 Wi-Fi、音频等驱动的源码。我们可以通过更新驱动来提高各部件工作的稳定性和性能。

🚧 施工中…

最后祝各位搞机一路顺利!

好耶,终于写完第一篇了!(躺)(叹气)

【小白向】Android内核折腾计划(一)——起步 https://blog.crrashh.com/posts/14-android-kernel-startup.html

除特殊声明转载之外,本文由博主云萧原创且非 AI 生成内容,依据 CC BY-SA 4.0 许可协议授权,若需转载请注明出处及本声明。

相关推荐

365体育投注网 玩机丨教您4个清理功能,轻松应对日常使用
365体育投注网 古代的笄礼是什么礼仪

古代的笄礼是什么礼仪

📅 07-23 👁️ 2024
28365365备用网址 win11怎么打开Windows?如何进入系统界面?