Android设备的内核编译及添加功能

 

参考教程及其它

1
2
3
4
5
6
# 获取ROOT权限后可直接使用ADB获取当前系统的内核配置文件
adb pull /proc/config.gz D:\

# 较新版本的内核基本无需GCC仅靠Clang就能编译,而老版本内核需要GCC或Clang + GCC。具体需要哪个编译器哪个版本还得看内核而定
# 安卓14以上似乎可以采用纯Clang编译,但是依然还需要使用GCC的工具链,新设备(Android_12-Kernel_5.10)直接用通用内核就行了

配置及编译内核

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 配置编译所需环境
sudo apt install build-essential bison flex libssl-dev git python2-minimal

# 获取交叉编译工具
git clone --depth 1 https://github.com/kdrag0n/proton-clang
export PATH=$PATH:~/proton-clang/bin
export CROSS_COMPILE=aarch64-linux-gnu- CROSS_COMPILE_ARM32=arm-linux-gnueabi- CLANG_TRIPLE=aarch64-linux-gnu-

# 获取K30Pro的内核源码
git clone --depth 1 https://github.com/Kscope-Devices/android_kernel_xiaomi_lmi
cd android_kernel_xiaomi_lmi
make ARCH=arm64 CC=clang O=out lmi_defconfig
make ARCH=arm64 CC=clang O=out -j12

# 编译完成后的内核文件会保存在 out/arch/arm64/boot 文件夹中,名称为 Image dtbo.img

启用Docker支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# 在 arch/arm64/configs/lmi_defconfig 后面添加以下内容启用Docker支持:
CONFIG_NET_NS=y
CONFIG_PID_NS=y
CONFIG_IPC_NS=y
CONFIG_UTS_NS=y
CONFIG_CGROUPS=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_SCHED=y
CONFIG_KEYS=y
CONFIG_BRIDGE_NETFILTER=y
CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
CONFIG_NETFILTER_XT_MATCH_IPVS=y
CONFIG_NETFILTER_XT_MARK=y
CONFIG_NF_NAT=y
CONFIG_POSIX_MQUEUE=y
CONFIG_NF_NAT_IPV4=y
CONFIG_NF_NAT_NEEDED=y
CONFIG_USER_NS=y
CONFIG_SECCOMP_FILTER=y
CONFIG_CGROUP_PIDS=y
CONFIG_MEMCG_SWAP_ENABLED=y
CONFIG_IOSCHED_CFQ=y
CONFIG_BLK_DEV_THROTTLING=y
CONFIG_CGROUP_PERF=y
CONFIG_CGROUP_HUGETLB=y
CONFIG_HUGETLBFS=y
CONFIG_HUGETLB_PAGE=y
CONFIG_NET_CLS_CGROUP=y
CONFIG_CGROUP_NET_PRIO=y
CONFIG_CFS_BANDWIDTH=y
CONFIG_FAIR_GROUP_SCHED=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_IP_VS=y
CONFIG_IP_VS_NFCT=y
CONFIG_IP_VS_PROTO_TCP=y
CONFIG_IP_VS_PROTO_UDP=y
CONFIG_IP_VS_RR=y
CONFIG_SECURITY_APPARMOR=y
CONFIG_VXLAN=y
CONFIG_VLAN_8021Q=y
CONFIG_BRIDGE_VLAN_FILTERING=y
CONFIG_CRYPTO=y
CONFIG_CRYPTO_AEAD=y
CONFIG_CRYPTO_SEQIV=y
CONFIG_CRYPTO_GHASH=y
CONFIG_XFRM=y
CONFIG_XFRM_ALGO=y
CONFIG_INET_XFRM_MODE_TRANSPORT=y
CONFIG_IPVLAN=y
CONFIG_MACVLAN=y
CONFIG_NF_NAT_FTP=y
CONFIG_NF_NAT_TFTP=y
CONFIG_AUFS_FS=y
CONFIG_BTRFS_FS=y
CONFIG_BTRFS_FS_POSIX_ACL=y
CONFIG_DM_THIN_PROVISIONING=y

# 该行在内核配置中重复,删除它来消除重复的警告
sed -i '/# CONFIG_PID_NS is not set/d' arch/arm64/configs/lmi_defconfig

# 重新生成 out/.config 内核配置文件
make ARCH=arm64 CC=clang O=out lmi_defconfig

# 检测内核配置是否支持Docker等容器
wget -O ../check-config.sh https://raw.githubusercontent.com/moby/moby/master/contrib/check-config.sh
chmod +x ../check-config.sh
../check-config.sh out/.config

# Generally Necessary:表示必要的配置,enabled是开启,missing没开启
# Optional Features: 是可选配置,根据需要打开。

# 在上面操作完成后重新编译内核即可启用Docker支持

打包及刷入内核

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 获取打包所需文件
git clone --depth 1 https://github.com/osm0sis/AnyKernel3

# 把编译生成的 Image和dtbo.img 丢到 Anykernel3 目录中即可
cp android_kernel_xiaomi_lmi/out/arch/arm64/boot/Image AnyKernel3
cp android_kernel_xiaomi_lmi/out/arch/arm64/boot/dtbo.img AnyKernel3

# 修改 Anykernel3/anykernel.sh 文件相关配置来指定刷入时的操作
sed -i 's/do.devicecheck=1/do.devicecheck=0/g' AnyKernel3/anykernel.sh # 不检测设备的名称
sed -i 's!BLOCK=/dev/block/platform/omap/omap_hsmmc.0/by-name/boot;!BLOCK=auto;!g' AnyKernel3/anykernel.sh # 自动选择内核分区
sed -i 's/IS_SLOT_DEVICE=0;/IS_SLOT_DEVICE=auto;/g' AnyKernel3/anykernel.sh # 自动判断用哪个槽

# 在 Anykernel3 文件夹中执行 zip -r <压缩包名.zip> * 即可
sudo apt install zip
cd Anykernel3
rm -rf .git .github modules patch ramdisk LICENSE README.md
zip -r ../LMI_Kernel.zip ./*

# 压缩打包前 AnyKernel3 目录的结构
leux@B650I:~/AnyKernel3$ tree
.
├── anykernel.sh
├── dtbo.img
├── Image
├── META-INF
│   └── com
│   └── google
│   └── android
│   ├── update-binary
│   └── updater-script
└── tools
├── ak3-core.sh
├── busybox
├── fec
├── httools_static
├── lptools_static
├── magiskboot
├── magiskpolicy
└── snapshotupdater_static

6 directories, 13 files
leux@B650I:~/AnyKernel3$

# 将打包好的内核传入手机内部
adb push .\Desktop\LMI_Kernel.zip /sdcard/Download

# 进入TWRP Recovery界面后,点击【安装】->【选中 LMI_Kernel.zip】->【刷入即可】,它会自行选择刷入的区域
fastboot reboot recovery # 或者在开机时 同时按住电源键和音量加 进入Recovery模式

# 由于自己打包的内核需要通过TWRP Recovery刷入,不是的话可参考如下先刷入TWRP Recovery
adb reboot bootloader # 或者在开机时 同时按住电源键和音量减 进入Fastboot模式
fastboot flash recovery twrp-3.7.0_12-v7.7_A12-lmi-skkk.img

# 如果想刷回官方内核,在当前系统版本刷机包中找到 boot.img 文件,进入Fastboot模式将其下刷入手机后重启即可
fastboot flash boot boot.img
fastboot reboot

安装运行Docker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 首先去如下地址下载安装 termux 程序
https://f-droid.org/zh_Hans/packages/com.termux/

# 安装root源后安装Docker
pkg install root-repo # 程序Docker在root源中
pkg install tsu docker-compose # 安装docker-compose时会自动安装docker

# 目前Termux的Docker还有些缺陷,例如无法使用itptables、只能使用host网络(运行时加上 --net=host --dns=8.8.8.8 参数)等问题
sudo mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup # 开启Termux,手动挂载cgroup
sudo dockerd --iptables=false # 启动Docker daemon
sudo docker run hello-world # 测试Hello World
sudo docker run -d -p 80:80 --name myapache --net=host --dns=8.8.8.8 httpd # 测试Apache服务器

# 通过浏览器访问Ubuntu桌面,用户名:kasm_user 密码:password
sudo docker run -d --name ubuntu \
--net=host --dns=223.5.5.5 -u=0:0 \
-v=/sdcard:/mnt:rslave \
--shm-size=512m -p 6901:6901 \
-e VNC_PW=password -e VNC_USE_HTTP=0 \
--restart unless-stopped \
linkease/desktop-ubuntu-standard-arm64

错误及其他知识

  1. 编译内核过程中在执行 scripts/dtc/mkdtboimg.py 脚本时需要用到Python2,Debian系可安装 python2-minimal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 在 scripts/Makefile.lib 中第320行内容: cmd_mkdtimg = python2 $(srctree)/scripts/dtc/mkdtboimg.py create $@ --page_size=4096 $(filter-out FORCE,$^)
OBJCOPY arch/arm64/boot/Image
DTBOIMG arch/arm64/boot/dtbo.img
/bin/sh: 1: python2: not found
make[2]: *** [../arch/arm64/boot/Makefile:74:arch/arm64/boot/dtbo.img] 错误 127
make[1]: *** [arch/arm64/Makefile:187:dtbo.img] 错误 2
make[1]: 离开目录“/home/leux/android_kernel_xiaomi_lmi/out”
make: *** [Makefile:146:sub-make] 错误 2

# 在 Debian 12 中 Python2 已经完全移除了,只能源码编译。因为 Python2 官方也已正式停止维护了,最后一个版本为 Python 2.7.18
wget https://www.python.org/ftp/python/2.7.18/Python-2.7.18.tar.xz
tar -xJf Python-2.7.18.tar.xz && cd Python-2.7.18

./configure --prefix=/opt/python2.7 # 添加参数 --enable-shared 或 --enable-optimizations 会导致编译通不过
make -j12
sudo make install # 这种编译方式安装好后只能提供基本的 Python2 功能
sudo ln -s /opt/python2.7/bin/python2 /usr/bin/python2

  1. 如何修改内核版本号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 刷入后的内核版本信息对比,系统全部参数与信息中【内核版本】显示:4.19.157-Kitsune-g60193e982594-dirty
lmipro:/ $ cat /proc/version # MIUI 14.0.4.0 (SJKCNXM) 稳定版原内核
Linux version 4.19.157-perf-g8779875ad741 (builder@non-pangu-pod-g0sww) (clang version 10.0.7 for Android NDK, GNU ld (binutils-2.27-bd24d23f) 2.27.0.20170315) #1 SMP PREEMPT Mon Apr 17 11:47:32 CST 2023

lmipro:/ $ cat /proc/version # 如上默认编译生成的内核
Linux version 4.19.157-Kitsune-g60193e982594-dirty (leux@B650I) (Android (7284624, based on r416183b) clang version 12.0.5 (https://android.googlesource.com/toolchain/llvm-project c935d99d7cf2016289302412d708641d52d2f7ee), GNU ld (GNU Binutils for Debian) 2.40) #12 SMP PREEMPT Mon Mar 3 12:03:28 CST 2025
lmipro:/ $

# 那为什么尾部会带 -dirty 后缀呢?这是因为更改源码后没有 git commit 导致的,用如下方法后重新编译即可去除该后缀
# 执行 git status 查看是否有更改,然后执行 git commit -s -a -m "getting rid of -dirty" 提交即可
# 也可以修改源码根目录下的 .gitignore 文件,将你需要忽略修改的目录或文件添加进来(每行一个),例如:/out 和 arch/arm64/configs/lmi_defconfig

# 如果不想有 -g60193e982594-dirty 也可使此项内核配置的值为 CONFIG_LOCALVERSION_AUTO=y,然后在内核源码根目录下创建名为 .scmversion 的空白文件即可
# 因为既没有这项值也没有 .scmversion 这个空白文件的话内核版本末尾就会有个 + 号,但若该文件存在则会 cat .scmversion 来获取该文件中的内容作为后缀信息

# 那么怎样才能将内核版本中的 -Kitsune 改为你想要的呢?修改 CONFIG_LOCALVERSION="-Kitsune" 内容为你要的即可

# 例如我想要编译后的内核版本显示为 4.19.157-leux 该如何操作呢?
echo "" > .scmversion
CONFIG_LOCALVERSION="-leux"
CONFIG_LOCALVERSION_AUTO=y

# 例如我想要编译后的内核版本显示为 4.19.157-perf-g8779875ad741 该如何操作呢?
echo "-g8779875ad741" > .scmversion
CONFIG_LOCALVERSION="-perf"
CONFIG_LOCALVERSION_AUTO=y

# 通过命令 make ARCH=arm64 CC=clang O=out kernelrelease 可直接显示编译后内核的版本号

# 要内核版本显示为 4.19.157-perf-g8779875ad741 也可先将CONFIG_LOCALVERSION="-perf" 和 # CONFIG_LOCALVERSION_AUTO is not set
# 然后修改 scripts/setlocalversion 中第206行 res="$res${scm:++}" 中的 + 为你想要的例如:res="$res${scm:+-g8779875ad741}"
if test "$CONFIG_LOCALVERSION_AUTO" = "y"; then
res="$res$(scm_version)"
else
if test "${LOCALVERSION+set}" != "set"; then
scm=$(scm_version --short)
res="$res${scm:++}" # 上面说的【内核版本末尾会有个 + 号】就是这里产生的
fi
fi

  1. Termux通过SSH登录
1
2
3
4
5
6
7
8
9
pkg install openssh			# 安装SSH相关程序
whoami # 查看当前用户名,例如:u0_a217
passwd # 为当前用户设置密码用来SSH登录
#cat rsa.pub >> ~/.ssh/authorized_keys # 也可选择添加公钥来免密登录
sshd # 启动SSHD的服务,默认端口 8022
echo "sshd" >> ~/.bashrc # 在打开Termux时自启SSHD服务

ssh u0_a217@192.168.1.196 -p 8022 # 在其他设备远程登录Termux

  1. 解决 Termux 下 Docker 或 LXC 中桥接模式不可用的问题
1
2
3
4
5
6
7
8
9
10
11
12
# 参考教程:https://gist.github.com/FreddieOliveira/efe850df7ff3951cb62d74bd770dce27

pkg install iptables # 需要安装iptables

# 以root权限执行以下命令获取默认的网关地址后添加路由,如果默认网关没有请自行设置。例如:GATEWAY_IP=192.168.1.1
GATEWAY_IP=$(ip route | grep wlan0 | grep default | awk '{print $3}')
ip route add default via $GATEWAY_IP dev wlan0
ip rule add from all lookup main pref 30000

# 可解决执行命令 sudo dockerd 启动服务后默认使用 bridge 模式的联网问题,而不需要 --net=host 参数且 docker 也可以直接映射端口
sudo docker run -it --name alpine -v=/sdcard:/mnt:rslave alpine:latest /bin/sh

  1. 关于挂载cgroup相关命令
1
2
3
4
mount -t tmpfs -o mode=755 tmpfs /sys/fs/cgroup
mkdir -p /sys/fs/cgroup/devices
mount -t cgroup -o devices cgroup /sys/fs/cgroup/devices

  1. 安装可视化管理界面Portainer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 首先将桥接网络弄通,这里网关为 192.168.1.1
ip route add default via 192.168.1.1 dev wlan0
ip rule add from all lookup main pref 30000

# 然后在Termux中运行Docker后台服务
sudo dockerd

# 最后运行中文可视化管理界面Portainer,通过 http://[WLAN_IP]:9000 访问WEB管理界面
docker run -d --restart=always --name=portainer -p 9000:9000 -v /data/data/com.termux/files/usr/var/run/docker.sock:/var/run/docker.sock 6053537/portainer-ce


# Debian下的 /lib/systemd/system/docker.service 中Docker启动命令如下
/usr/sbin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

# 参数解析
-H, --host list Daemon socket(s) to connect to # 指定 docker.sock 路径
--containerd string containerd grpc address # 指定containerd.sock路径

# 查询可知Debian和Termux上的Docker的sock路径分别如下:
/var/run/docker.sock # Docker守护进程的API
/run/containerd/containerd.sock # containerd 主服务,对外提供 gRPC 接口
/data/data/com.termux/files/usr/var/run/docker.sock
/data/data/com.termux/files/usr/var/run/docker/containerd/containerd.sock