【解剖】Googlebook Linux環境

Lenovo Googlebook 15 実機検証:Googlebook の Linux環境を解剖(内部構造の答え合わせと現状の運用法) Googlebook

今回はGooglebookのLinux環境が現状どうなっているのか、どのように運用できるのかについてまとめたいと思います。

はじめに

アーキテクチャ記事で予想していましたが、構造的にはほとんどその通りだったものの、現状まだ制限がかけられている箇所もありました。前回の記事では、Googleの公式アナウンスとカンファレンス(LPC 2025)で発表された「Ferrochrome」の資料をもとに、GooglebookのLinux環境がどのような仕組みで動くのかを推測しました。

その後、実際に手元に届いた実機(Lenovo Googlebook 15:Intel Core Ultra 5 325 / RAM 16GB / SSD 512GB)のターミナルからシステム内部を調査し、予想していた各項目が実際どうなっているのかを一つずつ確認しました。

本記事では、まず「事前のアーキテクチャ予想に対する実機の答え合わせ」を行い、そのうえで「現時点で制限されている箇所と、それを踏まえた実際の運用方法」について整理します。

予想記事と実機の構造比較(一覧表)

まず結論として、検証結果を一覧表にまとめます。

以下、主要なポイントについて実際のターミナル調査結果とともに見ていきます。

1. 基盤構造とDebian環境の自由度

マザーボードの製造元(sys_vendor)が ChromiumOS、製品名が crosvm となっており、予想通りハードウェア直上でメモリ空間を仕切るハイパーバイザーpKVM(protected KVM)の隔離基盤上で、Android ホスト側の crosvm が仮想マシン(Non-protected VM)として Debian 13 (trixie) を管理・稼働させています。

また、以前 ASUS Chromebook CM3206の記事 で取り上げた仮想アドレス空間について、本機はx86アーキテクチャであるCore Ultra 5 325 搭載であり 48 bits virtual が確保されています。Armアーキテクチャのマシンは手元にないため検証できていません。しかしアドレス空間制限によるアプリの起動トラブルは情報として持っていないためおそらくないと思われます。

ゲストOSの構造としては、旧来の ChromeOS(Crostini:親VMの Termina 内に LXC コンテナ penguin を作る二重構造)ではなく、ChromeOS 147(Baguette)で導入された「LXC コンテナ階層を介さず VM に直接 Debian を乗せるコンテナレス VM 構成」がそのまま踏襲されています。 Termux のような Android アプリ層の疑似環境とは異なり、VM 内で PID 1 として本物の systemd が稼働し、完全な root 権限が使えます。

この結果から、Googlebook の Linux カーネル(6.12.93-android16)の設計方針が見えてきます。

  • 16個の Virtio 1.0 デバイスによるホスト連携(00:01.0〜00:10.0): 画面描画(GPU)、コンソール(3系統)、仮想ディスク(2系統)、乱数生成器(RNG)、入力機器(4系統)、メモリ風船(balloon)、ネットワーク(network)、ホスト通信ソケット(socket=vsock)、ファイル共有(file system=virtiofs)、音声(sound)まで、合計16個の virtio デバイスが PCI バス上に並んでいます。
  • コンテナ機能や virtio ドライバはカーネル本体に直組み込み(=y): Docker 等のコンテナ稼働に必要な CONFIG_CGROUPS=y、CONFIG_NAMESPACES=y、CONFIG_OVERLAY_FS=y や CONFIG_VIRTIO_PCI=y は最初からカーネル本体に組み込まれているため、制限なく動作します。
  • ただし /lib/modules/ は存在せず、物理周辺機器のドライバは省かれたスリム構成(is not set): 一方で、通常の Linux PC にある /lib/modules/(後から modprobe で読み込む追加カーネルモジュール群)は存在しません。仮想マシンに必要な機能だけが組み込まれ、光学ドライブの /dev/sr0 ドライバ(CONFIG_BLK_DEV_SR)やマイコン用 USB シリアルドライバ(CONFIG_USB_SERIAL_CH341)といった物理ハードウェア向けのドライバは最初から省かれている(is not set)という割り切りがあります。ここは Chrome OS と同じです。

2. 仕組みは入っているが「標準状態では制限されている」3つの箇所

冒頭で触れた「構造的には予想通りだったものの、現状まだ制限がかけられている箇所」は主に以下の3点です。興味深いのは、機能が削られているのではなく、ゲストOS側にはすでにプログラム一式が入っているものの、標準状態ではホスト側の起動フラグによってオフにされているという点です。

GPUアクセラレーション(「GPU描画 ⇔ CPU描画」の切替機構は入っているが、現状はホスト側フラグがOFF)

Linuxログイン時に自動実行されるディスプレイ設定スクリプト(/etc/profile.d/activate_display.sh)を確認すると、「GPUハードウェア描画(enable_gfxstream)」と「CPUソフトウェア描画(enable_display)」を自動で切り替える仕組みが既に実装されていました。

上の4つのコマンド結果から、以下の事実が順番に読み取れます。

  1. dmesg の結果(virtio-gpu の機能フラグと8GB共有メモリ窓): 従来の ChromeOS で使われていた virgl は無効(-virgl)になり、代わりに Linux 側と Android ホスト側の間で画面データをコピーなし(ゼロコピー)で直接見せ合うための設定(+resource_blob +host_visible)と、最大8GB分の共有メモリ窓(Host memory window: +0x200000000)が確保されています(※物理メモリを最初から8GB消費しているわけではなく、PCIeアドレス空間上に8GB分の共有用番地が予約されています)。一方で、3Dコンテキスト初期化は -context_init(オフ)となっています。
  2. activate_display.sh と2つの設定ファイル(enable_gfxstream / enable_display)の中身: 起動引数(/proc/cmdline)に gfxstream_enabled があれば enable_gfxstream を読み込み、OpenGL を Vulkan に変換する zink と gfxstream_vk_icd.json(GPUハードウェア描画ドライバ) を使う設定になります。一方、無ければ enable_display を読み込み、LIBGL_ALWAYS_SOFTWARE=1 と lvp_icd.json(Mesa の CPU ソフトウェアレンダラ llvmpipe / lavapipe) を使う設定になります。
  3. cat /proc/cmdline の結果(現状の起動引数): 実際の /proc/cmdline を見ると gfxstream_enabled フラグが含まれていません(ターミナルアプリの「詳細設定」にもまだこのスイッチは用意されていません)。そのため、現時点では自動的に enable_display 側が選ばれ、CPU ソフトウェア描画+共有メモリ窓による画面転送で動作しています。

新型 Rust 製「Sommelier (0.1.0)」の存在と日本語入力(IME)

アーキテクチャ記事で「ChromeOSでは sommelier がウィンドウ管理や日本語IME連携を一手に引き受けていたが、Ferrochromeではどうなるか」という点を挙げていました。実機内を調べたところ、/usr/local/bin/sommelier に Rust 言語でゼロから書き直された新型の sommelier 0.1.0 が同梱されていました。

実行ファイル(バイナリ)の中に埋め込まれているソースコードのパスを strings コマンドで抽出すると、以下のことが分かります。

  • ChromeOS の C++ 製ではなく、Rust 言語で新規開発された sommelier であること: 拡張子 .rs(Rust のソースファイル:sommelier/src/main.rs や virtgpu_channel.rs など)が記録されており、Rust でゼロから書き直された新型であることが確認できます。
  • 個別ウィンドウ管理や日本語IMEプロトコルが組み込まれていること: ビルド生成物の中に、Wayland の個別ウィンドウ管理(xdg_shell_protocol.rs)、日本語IME入力プロトコル(text-input-unstable-v3_protocol.rs)、クリップボード共有(data_device.rs)が含まれています。

そして sommelier.service には ConditionKernelCommandLine=wayland_bridge という起動条件が設定されており、ターミナルアプリの「設定 > 詳細設定」にある 「試験運用版: マルチウィンドウの Wayland GUI アプリ」(標準ではOFF)をオンにした際に動くバックエンドとして用意されています。

標準設定(OFF)の状態では、入力系は crosvm から4系統の virtio-input(タッチ画面、キーボード、マウス、トラックパッド)としてカーネルに直接接続されており、物理キーコードがそのまま Linux に渡る仕組みになっています。そのため、初期状態では Android 側の日本語変換(Gboard等)は直接入らず、Linux 側に fcitx5-mozc 等の日本語IMEを導入する必要があります。

USBパススルー(カーネル側は /dev/bsg/* まで対応済みだがUI未開放)

先ほどの lspci で確認できた PCIe ホットプラグスロット(00:12.0)に加え、カーネル設定(/proc/config.gz)と USB デバイス一覧を確認した結果が以下です。

USB 3.0 ホストコントローラドライバ(CONFIG_USB_XHCI_PCI=y)や、先日公開した ChromeOSでのCD/DVD再生記事 で使用した SCSI Generic v4(CONFIG_BLK_DEV_BSG=y:/dev/bsg/*)はすべてカーネル本体に組み込まれていました。

ただし、現時点では Android 側のターミナルアプリに「USB機器をLinuxへ接続する」ための設定項目がまだ用意されておらず、/sys/bus/usb/devices/ は空(total 0)の状態です。カーネル側の受け皿はすでに整っているため、今後のOSアップデートでのホスト側UI開放を待つ形になります。

一方で、パススルーが塞がっている現状における代替手段として、ネットワーク経由の仕組みがあります。

カーネルレベルの USB 共有機構である USB/IP(CONFIG_USBIP_CORE)は無効(is not set)ですが、TCP/IP プロトコルスタック(CONFIG_INET=y)と仮想ネットワークドライバ(CONFIG_VIRTIO_NET=y)はカーネルに組み込まれています。

そのため、直接の USB パススルーが塞がっている現状でも、Android ホスト側で物理 USB デバイスを接続し、TCP 通信で Linux 側へやり取りを中継する形であれば代用が可能です。

3. 動作している箇所

一方で、ファイル共有、メモリ・ディスクの動的管理、ネットワーク連携については、稼働しています。

ファイル共有(設定画面から選択したフォルダを virtiofs で /mnt/shared にマウント:最大 592 MB/s)

ファイル共有については、Android の内部ストレージ全体が最初から見えているわけではなく、ターミナルアプリの 「設定 > 共有フォルダ」 の画面(右上の「+追加」ボタン)から、ユーザーが指定・許可したフォルダだけが Linux 側の /mnt/shared 内に表示される仕組みになっています。

実際に /mnt/shared のマウント状態と、100MBファイルの書き込み速度を連続測定した結果が以下です。

  • android on /mnt/shared type virtiofs:設定画面で追加した Android 側のフォルダ(上の例では Antigravity フォルダ)が、virtiofs によって /mnt/shared 内にマウントされています。
  • 書き込み速度は実測 193〜592 MB/s(100MBを約 0.17〜0.54 秒で書き込み):複数回連続で測定するとおよそ 193〜592 MB/s が出ており、同じ 100MB 連続書き込みを Linux 内部の ext4 領域(/home/droid)で行った場合(137〜635 MB/s)と変わらない速度で読み書きできます。

なお、mount の結果(rw,relatime)の通りマウントオプションに dax は指定されておらず、単一の大きなファイルの書き込みは高速な一方で、多数の小さなファイルを一気に生成・削除する処理では Linux 内部の ext4 領域と比べて以下のように差が出ます。

200個の空ファイルを生成・削除すると、内部 ext4 が約 0.01〜0.03 秒で終わるのに対し、共有フォルダ(virtiofs)上ではゲスト・ホスト間の通信回数が増えるため約 1.5〜1.7 秒かかります。なお、Googlebook の Antigravity 環境では最初からこの特性を考慮した標準ルールが組み込まれており、ソースコードは共有フォルダに置いたまま、大量の小ファイルが発生するビルドキャッシュ類は自動的に Linux 内部の ext4 領域(~/.cache/projects/)へ逃がして遅延を回避する仕組みになっています。

メモリ容量のユーザー設定(1.0GB〜10.8GB)と「風船(Balloon)」による動的管理

従来の ChromeOS では Linux に割り当てるメモリ容量をユーザーが設定画面から変更することはできませんでしたが、Googlebook のターミナルアプリでは 「設定 > 詳細設定 > メモリ容量」から、スライダーまたは数値入力で 1.0 GB 〜 10.8 GB(10,882 MB)までユーザーが自由に上限を変更できるようになっています。

実際に上限を最大の 10.8 GB に設定した状態で Linux 内部を確認した結果が以下です。

メモリ上限を 10.8 GB に設定した状態で確認すると、約 10 GiB のメモリと約 2.6 GiB のスワップ領域が割り当てられており、/proc/vmstat からは virtio-balloon(風船機能)によるメモリの膨張・収縮(balloon_inflate / balloon_deflate)が実際に稼働していることが確認できます。

また、ストレージについても /etc/fstab に discard(TRIM)オプションが設定されており、Linux 内でファイルを削除した際に不要になったブロックをホスト側へ通知・返却する仕組みが有効になっています。

eBPF(tcpstates-libbpf)によるポート自動検知と「設定 > セキュリティ」画面との連携

前回の記事の表で「eBPFによる自動検知・ポート転送」と予想していた通り、Linux 内部ではゲストエージェント(/usr/bin/linux_vm_manager)の子プロセスとして /usr/sbin/tcpstates-libbpf が常駐し、検知されたポートがターミナルアプリの 「設定 > セキュリティ」 画面と連携する仕組みになっていました。

上のコマンド結果と設定画面を見比べると、以下のことがそのまま確認できます。

  1. tcpstates-libbpf の常駐と「プロセス名+ポート番号」の検知: ps -ef の結果から、/usr/bin/linux_vm_manager(PID 341)の子プロセス(親PID 341)として /usr/sbin/tcpstates-libbpf が常駐していることが分かります。そして journalctl の起動時ログを見ると、Starting monitoring ports の直後から python3(14580/14585/14586)、websockify(14650〜14655)、tailscaled(60410/62646)、sshd(2222)の各リスニングポートを linux_vm_manager(forwarder_guest_launcher)が検知しています。
  2. 「設定 > セキュリティ」画面での一括・個別制御: ログに記録された12個のポートは、そのままターミナルアプリの「設定 > セキュリティ」画面の下部にある 「リスニング ポート」一覧 にまったく同じ名前・ポート番号(python3 (14580)、sshd (2222) など)で表示されています。
    • 「すべてのポートの自動転送」を ON にした場合:検知されたポートが自動的にホスト側へ転送され、Android 側の Chrome 等からそのまま localhost:ポート番号 でアクセスできます。
    • 個別に制御したい場合:「すべてのポートの自動転送」を OFF にしたうえで、下の「リスニング ポート」一覧から必要なポートのスイッチだけを個別に ON/OFF できます。

4. 現状どのように運用できるのか

Linux GUIアプリですが、現状はデフォルトでは個別アプリ毎にWindowを起動する設計になっておらず、下記の画面に示すターミナルアプリの中でGUI Windowが立ち上がります。これだと使いづらいです。

なお、この Linux GUI アプリの表示については、2章で触れた GPU アクセラレーションや USB パススルーなどとあわせて、Google の公式ブログでも今後のアップデートで対応予定であることが公表されています。

そこで公式対応が整うまでの現状の回避策として、私は先ほどの「USBパススルー」の項目で示した TCP 通信を使い、自作アプリで運用しています。アーキテクチャは下記の通りです。

自作アプリ「Linux GUI Apps」の通信・画面表示アーキテクチャ
アプリ1個ごとに「専用の仮想モニター」と「専用のTCPポート」を割り当てて双方向通信
Android ホスト側(マルチウィンドウ)
① ランチャー画面(Linux GUI Apps)
アプリ一覧を表示し、タップで起動指示
② 独立ウィンドウ A(Nautilus)
映像受信 & マウス・キー・リサイズ送信
③ 独立ウィンドウ B(テキストエディタ)
別ウィンドウとして自由に並べて操作
自動ポート転送
tcpstates-libbpf
TCP 14580
⇄ 指令・一覧
TCP 14650
⇄ 映像・操作
TCP 14651
⇄ 映像・操作
Linux 仮想マシン(Debian 13)
司令塔サーバー(python3)
アプリ検出・個室起動・クリップボード同期
個室 :50(Xtigervnc + websockify)
Nautilus + fcitx5-mozc が稼働
個室 :51(Xtigervnc + websockify)
テキストエディタ + fcitx5-mozc が稼働
標準機能(ターミナル右上のディスプレイボタン)
1枚の「Weston 仮想デスクトップ」ウィンドウ
Linux アプリ A
Linux アプリ B
・この1枚の大きな枠の外へアプリを出せない
・Android アプリと個別に並べて配置しづらい
自作「Linux GUI Apps」(TCPポート個別配信)
Linux アプリ ATCP 14650
独立ウィンドウ
Android アプリChrome 等と
自由に並列
Linux アプリ BTCP 14651
独立ウィンドウ
・1アプリ=1仮想画面=1ポートで切り出し
・Android の普通のアプリと同じ感覚で個別配置

このようにアプリごとに個別の TCP ポートで通信することで、以下のように独立した Window が立ち上がります。

おわりに

今回は Googlebook の Linux 環境の内部構造を実機で確認し、現在の運用方法についてまとめました。今後の Googlebook OS のアップデートで実装予定の機能が正式版として順次開放されれば、手元の Linux 環境単体でもさらに多くの用途がこなせます。

なお、今回紹介した GUI ウィンドウ化アプリのほかにも、USB 機器を繋ぐブリッジや自分専用の AI コンソールなど、Googlebook で Antigravity を使っていくつかAndroid アプリを自作しています。

次回はそれらのアプリを紹介しつつ、実際に作る中で考えるようになった「Googlebook と Antigravity をめぐる Google の狙い」についても考察してみたいと思います。

コメント

タイトルとURLをコピーしました