はじめに

Linux カーネルを書き換えて TCP の再送時間のパレート最適性を裏切る方法はこの記事で実験を行った。 ただ私のような Mac ユーザーは Linux をサーバー用途でしか使わないため、カーネルを書き換えたところでそこまで得できない。 やはりこれを使うにはクライアント端末で Kernel を書き換える方が楽しい。 私が普段使ってるクライアント端末で Kernel を書き換えられそうなもの、そう Android である。 この記事では自分の持ってるスマホに、私が書き換えた kernel をインストールして実験してみる。 (この記事はただの検証記事であり TCP のプロトコルを裏切ることを推奨するものではありません。私も書き換えたカーネルを普段使いしているわけではありません。)

Android をソースからビルドする方法

この記事では PixelExperience という Android の Custom firmware のビルド方法を紹介する。 ビルドに用いたマシンは、OS: Ubuntu 22.04 LTS, メモリ: 16GB (+ Swap 32GB)である。 以下の内容は、この記事を参考にした。

fastboot や adb や git や python をインストールする。

sudo apt install android-sdk git python-is-python3

android をビルドするための依存関係全部インストールするスクリプト実行する。

git clone https://github.com/akhilnarang/scripts
cd scripts
./setup/android_build_env.sh

ビルド用のディレクトリを作る。

mkdir -p ~/android/pe

git の設定を作る。(すでにやってたら必要なし)

git config --global user.email "you@example.com"
git config --global user.name "Your Name"

~/android/pe を repository として初期化。

cd ~/android/pe
repo init -u https://github.com/PixelExperience/manifest -b twelve-plus

ソースをすべてダウンロード

repo sync -c -j$(nproc --all) --force-sync --no-clone-bundle --no-tags

ディバイス固有のソースファイルをすべてダウンロード

source build/envsetup.sh
lunch aosp_sweet-userdebug

ccache を用いてビルドをキャッシュする。 .bashrc に書き込む。

export USE_CCACHE=1
export CCACHE_EXEC=/usr/bin/ccache
export CCACHE_DIR=~/.ccache

ccache の容量を指定し、圧縮を有効化。

ccache -C
ccache -M 50G
ccache -o compression=true

あとはビルド

croot
mka bacon -j$(nproc --all)

boot.img のみビルド

mka bootimage -j$(nproc --all)

echo $OUTにビルドした installer package や、boot.img があるはずなのでそれを twrp などで端末に突っ込んだら終了。 ちなみに、kernel を書き換えるためには、boot.img のみをインストールすれば良い。

adb, fastboot のコマンド集

ここで adb と fastboot でよく使うコマンドをまとめておく

まず、端末が adb の命令を受け取れる状態かを確認する。

adb devices

再起動、リカバリーモード、fastboot モードを起動

adb reboot
adb reboot recovery
adb reboot fastboot

ファイルを転送(例えば boot.img を移す方法)

adb push out/target/product/sweet/boot.img /sdcard/

Android 内の Shell を叩く(内部的には toybox なので最低限のコマンドしか叩けない)

adb shell ls

fastboot モードで twrp を起動(起動のみで書き込まれない。twrp の画面から flash 出来る。)

fastboot boot twrp-3.6.2_9-0-tissot.img

これだけ覚えとけば大丈夫

kernel をビルドしてインストール

まず、~/android/pe/kernel/vender/device/内にカーネルのソースコードがあるので、以下に対応する部分を書き換える。

https://github.com/torvalds/linux/blob/v5.15/include/net/tcp.h#L139

変更前

#define TCP_RTO_MAX	((unsigned)(120*HZ))

変更後

#define TCP_RTO_MAX	((unsigned)(1*HZ))

ビルド

mka bootimage -j$(nproc --all)

ファイルを転送(例えば boot.img を移す方法)

adb push out/target/product/sweet/boot.img /sdcard/

adb reboot recoveryで twrp の画面から kernel をインストールする。

検証

さて、192.168.0.20 に 23 番ポートを塞いだサーバーを用意する。 このサーバーに対してnetcatを用いて tcp の通信を試みる。 具体的には以下のコマンドで計測を行った。

adb shell time  netcat 192.168.0.20 23

kernel の書き換え前と書き換え後で time out までの時間を計測した。

kernel の書き換え前

$ adb shell time  netcat 192.168.0.20 23
netcat: connect: Connection timed out
    0m31.26s real     0m00.01s user     0m00.01s system

kernel の書き換え後

$ adb shell time  netcat 192.168.0.20 23
netcat: connect: Connection timed out
    0m05.09s real     0m00.00s user     0m00.01s system

前回の実験と合わせて、Android はnet.ipv4.tcp_syn_retries = 4に設定していることがわかる。 その上で再送の間隔が指数関数的に伸びないように変更出来た。

感想

  • Android のビルドしてインストールは以外と簡単に出来た。(数時間 CPU 使用率 100%だったが。)
  • Android の linux kernel は普通の linux kernel と結構似ているので意外と遊びの幅が広いかもしれない。
  • 古い Android 端末向けに最新 Android をビルドするとかやってみたい。