一次云服务器内核升级之旅
Keep Team Lv4

  最近突发奇想,想尝试在本就不富裕的小云上搭建一个云手机平台。于是在github上不停的搜关键字(这里吐槽一下gitee的搜索功能做得还是太弱了),终于让我找到了两个不错的开源项目,anboxredroid

  我们都知道Android是分层的,按语言可简单分为Java和Native,按类别可分为application和service,他们之间的通信几乎都是利用ashmem或binder完成。而这2个功能又需要内核支持。所以不管是那种容器(容器与主机共用同一个内核)方案,内核都需要支持ashmem和binder。遗憾的是我的小云运行的是CentOS7,内核是3.10。默认不开启ashmem和binder功能的。所以只有一条路可走,手动编译适合的内核(yum虽能安装高版本内核,但是不确认是否已开启这2个特性)。

  通过以下命令可确认是否支持ashmem和binder。

#check modules status
lsmod | grep -e ashmem_linux -e binder_linux
# example output:
# binder_linux          147456  79
# ashmem_linux           16384  23

# we can also check like this
grep binder /proc/filesystems 
# output should like: nodev	binder
grep ashmem /proc/misc 
# output should like: 56 ashmem

注:centos7 默认开启binder之后,/dev下可能也没有binder节点,可以通过手动mount的方式进行确认。

sudo mkdir /dev/binderfs
sudo mount -t binder binder /dev/binderfs

内核编译

内核下载

  首先要挑选合适的内核版本下载,可以通过kernel.org官网或国内镜像(如:阿里云)进行下载。

  内核版本并不是越高越好,经过血泪教训,建议一定选择长周期版本,防止内核抽风(之前用5.17.3,系统老是Panic重启,让人相当的崩溃)。

  内核源码可通过wget下载到本地某个目录下,然后用tar解压即可(属于基操就不多说了)。

内核配置

  Linux的内核有太多配置项需要配置,且大多数配置项可能都不知道是用来干嘛的(到底应该开还是不开?懵逼ing)。所以建议在配置前先将当前的内核配置文件拷贝过来,这样只需要根据需求做增量修改置即可。

当前使用的内核配置依据系统的不同可能在不同的地方

/proc/config.gz
/boot/config-$(uname -r)
/usr/src/linux-$(uname -r)/.config
/usr/src/linux/.config

  找到配置之后将其重命名为.config,并拷贝至内核源码目录下即可。因为是在云上操作,只能通过ssh登录字符界面,那么可通过make menuconfig打开一个字符图形界面来配置内核。

内核配置

备注:上图只是一个示例,Android相关的配置在红框内可以找到。

  除了使用界面来配置内核,还有更简单的方式,即直接在.config文件中加入想要打开的配置项,比如开启Android相关的开关,可以添加如下内容:

CONFIG_ASHMEM=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_ANDROID_BINDERFS=y
CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder,vndbinder,binderfs"
CONFIG_ANDROID_BINDER_IPC_SELFTEST=y

详细的配置已放在Gitee

内核编译

  配置好之后,就可以在内核源码所在目录执行make,开始进行内核编译。这一步会生成内核模块和vmlinuz,initrd.img,Symtem.map等文件。

强烈建议:make时带上参数LOCALVERSION,以便于更好的区分内核。如:

make LOCALVERSION=android

  网上有资料写还需要执行make modules什么的,其实完全不用。在不特点指定make对象的时候,make会自动编译完所有内容。

  编译成功后需将模块(ko文件)以及内核镜像等文件拷贝至系统目录。执行make modules_installmake install即可。

  modules_install会在/lib/modules目录下新建一个与本次编译内核同名的文件夹,并将编译生成的ko文件拷贝进来。

[core@VM-16-15-centos modules]$ pwd
/lib/modules
[core@VM-16-15-centos modules]$ ls
3.10.0-1160.62.1.el7.x86_64  5.4.190-android
[core@VM-16-15-centos modules]$ ls 5.4.190-android/
build              modules.builtin.bin      modules.order
kernel             modules.builtin.modinfo  modules.softdep
modules.alias      modules.dep              modules.symbols
modules.alias.bin  modules.dep.bin          modules.symbols.bin
modules.builtin    modules.devname          source
[core@VM-16-15-centos modules]$

  install则会将内核镜像文件拷贝至启动分区(/boot)。

  至此,内核编译与安装就完成了,不过此时重启系统并不会用新的内核进行引导,还需要修改引导顺序。

内核切换

  Linux内核一般通过GRUB进行引导,发展到今天GRUB已经很稳定了(很久以前装Linux和Windows双系统,稍不注意就把GRUB搞砸了),对于内核升级的场景也不用特别担心,按步骤修改即可。

awk -F\' '$1=="menuentry " {print $2}' /etc/grub2.cfg
grub2-set-default '$your_kernel_name_or_index'
vim /etc/default/grub
grub2-mkconfig -o /boot/grub2/grub.cfg
  1. 获取当前内核列表(在上一步make install后,会刷新信息),列表是有序排列,最顶上序号为0,往下依次加1。请记住需要切换内核的序号以及名称。
  2. 配置默认内核信息,通过grub2-set-default进行设置,参数可以是名字也可以是序号。
  3. 修改配置文件(/etc/default/grub),刷新文件中默认引导内核的序号。
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=0
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL="serial console"
GRUB_TERMINAL_OUTPUT="serial console"
GRUB_CMDLINE_LINUX="crashkernel=auto console=ttyS0 console=tty0 panic=5 net.ifnames=0 biosdevname=0 intel_idle.max_cstate=1 intel_pstate=disable"
GRUB_DISABLE_RECOVERY="true"
GRUB_SERIAL_COMMAND="serial --speed=9600 --unit=0 --word=8 --parity=no --stop=1"

  关键是修改GRUB_DEFAULT=序号。完成修改后就可以重新生成GRUB引导配置了。

  1. 执行grub2-mkconfig -o /boot/grub2/grub.cfg即可根据新的培训信息生成并覆盖之前的文件了。

  有时候编译出来的内核可能与预期不符,当然我们可以继续编译一个新的同版本的内核(使用不同的后缀),然后再切换至新内核上,但是如果当前正在使用的内核与废弃的内核是同一个版本,此时是不允许被删除的(意味着系统得一直带着这个废弃的内核),所以我们需要对内核做回滚。也正因为如此,所以在主机上最好保留一个原始的内核版本,以便在需要时能实现回滚

内核删除

  当新内核能满足我们的需要之后,就可以删除老旧的内核了。对于系统自带或通过包管理器安装的内核,可以通过命令(比如:apt或yum等)的方式直接删除。但是对于自己编译的内核就只能通过手动的方式进行删除操作了。

删除前需确认该内核当前未在使用

  1. 移出modules
    sudo rm -rf /lib/modules/<you_want_remove_kernel>
  2. 删除boot img
    sudo rm -rf /boot/*<key_word_in_your_kernel>*

  由于img文件在boot中会比较多,所以删除img文件,推荐使用通配符*,进行删除。

  1. 重新生成grub.cfg

  这一步并非必须,只不过不做的话,在启动菜单中会包含无效内核而已。

  另外如果删除内核后会影响内核排序,那么建议参考上文【内核切换】,在删除内核文件后,重新做一次引导信息刷新。

grub2-mkconfig -o /boot/grub2/grub.cfg

  从删除的方式可以看出移出自己编译的内核,其实就是从系统目录删除相应的文件即可。