在Android系统中,其所使用的Kernel是在Linux Kernel的基础上增加了Android独有的内容,如binder驱动等。同时Kernel代码也并非在AOSP的源码内,而是一个单独的repo。在AOSP源码中,内核均为编译好的二进制文件,用户是无法直接修改的。但有些时候我们不得不单独编译Android Kernel来实现自己的需要,如涉及到驱动修改等。今天这篇文章即是对此的一个简单记录。
具体的内核编译分为以下几个步骤:
1)拉取源码
1.mkdir android-kernel && cd android-kernel
2.repo init -u https://android.googlesource.com/kernel/manifest,在这里,我们可以使用-b参数来指定具体的分支repo sync拉取代码
3.repo sync -j8 进行拉取
2)源码编译
在Android11之后,内核编译分为两个部分:GKI内核部分与可动态配置的第三方模块部分,其中GKI为Google维护的通用内核部分,而可动态配置的第三方模块部分则是由供应商或者第三方自由配置的部分。
为什么需要这样分配呢,这是因为原本的Android Kernel属于宏内核,其组成包括以下几部分:
1.来源于Linux Kernel的上游代码
2.来源于AOSP通用内核中针对Android专用的内核代码
3.来源于供应商提供的Soc外围设备支持代码(主要是外围设备的驱动支持)
4.来源于设备制造商(Soc开发方)的其他设备驱动程序或者自动改动代码
事实上来源于3,4的代码在Android Kernel中占用达到50%以上。这导致Android内核更新相当困难,尤其是当上游存在重大更新时。在引入GKI后,来源于上游Linux内核的代码与Google自身维护支持的代码将被集中打包,而剩下的部分则可以以模块配置的形式存在,可以自由裁剪,这样可以在一定程度上实现解耦的目的。
GKI(General Kernel Image)本身基于ACK(Android Common Kernel)来源进行构建。
我们首先需要编译GKI部分,如编译x86_64的GKI部分:
BUILD_CONFIG=common/build.config.gki.x86_64 build/build.sh
再编译第三方模块(此处以cuttlefish为例)部分,其实现代码均位于common-modules目录内:
BUILD_CONFIG=common-modules/virtual-device/build.config.virtual_device.x86_64 build/build.sh
无论是编译GKI还是模块部分,都依赖于具体的config配置
编译生成的产物位于out/BRANCH_NAME/dist/目录下,这里面包含很多ko文件与initramfs.img、bzImage(x86系统)或者Image(arm系统),之后我们需要打包成为boot.img。
如果你是使用Android Emulator或者是Android Cuttlefish虚拟设备,具体的使用方法上则有一些差异。在Android Cuttlefish虚拟设备中,可以通过如下命令来启动内核:
HOME=$PWD ./bin/launch_cvd -daemon -kernel_path /path/to/bzImage -initramfs_path /path/to/initramfs.img
3)其他
如何检查内核版本信息:通过cat /proc/verison来获取当前的版本信息,结果示例如下:
Linux version 5.10.66-android12-9-00021-g2c152aa32942-ab8087165 ([email protected]) (Android (7284624, based on r416183b) clang version 12.0.5 (https://android.googlesource.com/toolchain/llvm-project c935d99d7cf2016289302412d708641d52d2f7ee), LLD 12.0.5 (/buildbot/src/android/llvm-toolchain/out/llvm-project/lld c935d99d7cf2016289302412d708641d52d2f7ee)) #1 SMP PREEMPT Fri Jan 14 17:35:16 UTC 2022
这表明所使用的内核版本为5.10,我们也可以使用uname -a命令来获取版本内核信息:
5.10.66-android12-9-00021-g2c152aa32942-ab8087165
如何确定内核驱动模块是否加载:这里我们可以通过lsmod命令来观察,如下图所示:
trout_x86:/ $ lsmod | grep virtio
virtio_gpu 81920 11
vmw_vsock_virtio_transport 24576 6
vmw_vsock_virtio_transport_common 40960 1 vmw_vsock_virtio_transport
vsock 45056 12 vmw_vsock_virtio_transport,vmw_vsock_virtio_transport_common,vsock_diag
virtio_net 57344 0
net_failover 24576 1 virtio_net
virtio_pmem 16384 1
nd_virtio 16384 1 virtio_pmem
virtio_blk 28672 4
virtio_console 36864 0
virtio_rng 16384 0
virtio_input 20480 0
virtio_pci 28672 0
virtio_mmio 24576 0
trout_x86:/ $
这里还有一个问题,在Android编译时,是如何确定编译的内核版本与预编译产物的路径呢,这部分的规则其实都位于/device/目录下的makefile内,我们可以在makefile文件内全局搜索TARGET_KERNEL_USE与TARGET_KERNEL_PATH这两个变量,就能够找到对应的kernel版本以及其资源路径。
以上就是本篇博客的全部内容,希望能对各位有所帮助~