Physical Address:
ChongQing,China.
WebSite:
在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 进行拉取
拉取完成之后,我们可以看到其源码组织结构如下:
total 48
drwxrwxr-x 12 FranzKafka FranzKafka 4096 Oct 20 2022 .
drwxr-xr-x 37 FranzKafka FranzKafka 4096 Apr 23 16:10 ..
drwxrwxr-x 7 FranzKafka FranzKafka 4096 Oct 20 2022 build
drwxrwxr-x 25 FranzKafka FranzKafka 4096 Oct 20 2022 common
drwxrwxr-x 3 FranzKafka FranzKafka 4096 Oct 20 2022 common-modules
drwxrwxr-x 3 FranzKafka FranzKafka 4096 Oct 20 2022 hikey-modules
drwxrwxr-x 4 FranzKafka FranzKafka 4096 Oct 20 2022 kernel
drwxrwxr-x 3 FranzKafka FranzKafka 4096 Oct 20 2022 out
drwxrwxr-x 5 FranzKafka FranzKafka 4096 Oct 20 2022 prebuilts
drwxrwxr-x 3 FranzKafka FranzKafka 4096 Oct 20 2022 prebuilts-master
drwxrwxr-x 7 FranzKafka FranzKafka 4096 Oct 20 2022 .repo
drwxrwxr-x 3 FranzKafka FranzKafka 4096 Oct 20 2022 tools
大部分源码都位于common目录中,而common-modules中也包含部分虚拟化相关的代码。
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 (build-user@build-host) (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:/ $
如何手动加载/卸载驱动模块:如果我们需要手动加载/卸载模块,我们需要使用insmod/rmmod命令。实际上在Linux系统中,很多重要的模块都直接编译到了内核,但也有其他模块以*.ko的形式打包,可以在需要时通过insmod命令加载,这些模块在Linux系统中位于/lib/modules目录下,我们通过insmod命令进行安装,如下图所示:
root@bstcd-OptiPlex-7090:/lib/modules/5.14.0-1042-oem/kernel/drivers/virtio# ls -la
total 112
drwxr-xr-x 2 root root 4096 6月 15 2022 .
drwxr-xr-x 111 root root 4096 6月 15 2022 ..
-rw-r--r-- 1 root root 7673 6月 4 2022 virtio_dma_buf.ko
-rw-r--r-- 1 root root 19865 6月 4 2022 virtio_input.ko
-rw-r--r-- 1 root root 56169 6月 4 2022 virtio_mem.ko
-rw-r--r-- 1 root root 16945 6月 4 2022 virtio_vdpa.ko
root@bstcd-OptiPlex-7090:/lib/modules/5.14.0-1042-oem/kernel/drivers/virtio# insmod virtio_input.ko
root@bstcd-OptiPlex-7090:/lib/modules/5.14.0-1042-oem/kernel/drivers/virtio#
root@bstcd-OptiPlex-7090:/lib/modules/5.14.0-1042-oem/kernel/drivers/virtio#
root@bstcd-OptiPlex-7090:/lib/modules/5.14.0-1042-oem/kernel/drivers/virtio# lsmod | grep virtio
virtio_input 16384 0
vmw_vsock_virtio_transport_common 40960 1 vhost_vsock
vsock 45056 16 vmw_vsock_virtio_transport_common,vhost_vsock
root@bstcd-OptiPlex-7090:/lib/modules/5.14.0-1042-oem/kernel/drivers/virtio#
root@bstcd-OptiPlex-7090:/lib/modules/5.14.0-1042-oem/kernel/drivers/virtio# rmmod virtio_input.ko
root@bstcd-OptiPlex-7090:/lib/modules/5.14.0-1042-oem/kernel/drivers/virtio#
root@bstcd-OptiPlex-7090:/lib/modules/5.14.0-1042-oem/kernel/drivers/virtio# lsmod | grep virtio
vmw_vsock_virtio_transport_common 40960 1 vhost_vsock
vsock 45056 16 vmw_vsock_virtio_transport_common,vhost_vsock
root@bstcd-OptiPlex-7090:/lib/modules/5.14.0-1042-oem/kernel/drivers/virtio#
那么如果我们需要在Android系统中加载某个模块,可以使用adb shell insmod Modulename.ko加载特定的module.
除了insmod/rmmod命令,我们还可以使用modprobe命令,具体使用方法可自行搜索,此处略过。
如何确定Android使用的内核版本:这里还有一个问题,在Android编译时,是如何确定编译的内核版本与预编译产物的路径呢,这部分的规则其实都位于/device/目录下的makefile内,我们可以在makefile文件内全局搜索TARGET_KERNEL_USE与TARGET_KERNEL_PATH这两个变量,就能够找到对应的kernel版本以及其资源路径。
以上就是本篇博客的全部内容,希望能对各位有所帮助~