4 ポイント 投稿者 GN⁺ 2025-10-27 | 1件のコメント | WhatsAppで共有
  • Linuxカーネルを自分でビルドし、最小限のユーザー空間を構成して「マイクロLinuxディストリビューション」を作る手順を段階的に解説
  • オペレーティングシステムのカーネルの役割、Linuxディストリビューションの構成要素、そしてカーネルとユーザー空間の関係を基礎から扱う
  • RISC-Vアーキテクチャ(QEMUのriscv64 virtマシン)を例に使うが、x86など他のアーキテクチャにも同じ原理を適用可能
  • initプロセス、initramfs、そしてGoで書いた簡単なシェルを含む自分で実行できる最小Linux環境を構築
  • 最後にu-rootプロジェクトを使って実際に役立つマイクロディストリビューションを作る方法を紹介し、Linuxシステム全体の構造理解を助ける入門ガイドとして締めくくる

オペレーティングシステムのカーネルとは何か

  • カーネルはハードウェア資源の管理とプログラム実行の制御を担う、オペレーティングシステムの中核要素
    • 単一コア環境でも複数のプログラムが同時に動いているように見せるマルチタスク管理機能を提供
  • カーネルは入出力デバイス制御を抽象化し、アプリケーションがハードウェアの詳細なアドレスやレジスタ値を直接扱わなくて済むようにする
    • たとえばプログラムは単に「標準出力にメッセージを書け」と要求し、カーネルが実際のハードウェアとのやり取りを処理する
  • ファイルシステムインターフェースを通じて高水準のデータアクセス手段を提供
    • ファイルは単なるディスク上のデータではなく、カーネルと通信する論理インターフェースとして動作する
  • カーネルはプロセス間の分離と通信モデルを提供し、各アプリケーションが独立して動作したり協調したりできるようにする
  • Linuxカーネルはオープンソースで、さまざまなアーキテクチャで動作でき、世界で最も広く使われているカーネルの1つ

Linuxディストリビューションとは何か

  • LinuxカーネルだけではユーザーはWebブラウザやGUIアプリを実行できず、カーネルの上に複数層のソフトウェア基盤が必要
  • ネットワーク設定、IP割り当て、VPN管理などはカーネルではなく上位のユーザー空間プログラムが担当する
  • したがってLinuxディストリビューションはカーネル + ユーザー空間インフラの組み合わせとして定義される
  • ディストリビューションには、カーネルが提供する基本機能に加えて**パッケージ、ツール、設定、初期化プロセス(init)**などが含まれる
  • ディストリビューションの複雑さはさまざまで、Arch Linuxのような最小構成からUbuntuのようなユーザーフレンドリーな構成まで存在する

カーネル外部のインフラ: ユーザー空間とinitプロセス

  • カーネルがブートを終えると、最初に**PID 1のプロセスであるinit**を実行する
    • initはその後のすべてのユーザー空間プロセスの祖先となり、システムのサービスやツールを順に起動する
  • initが実行する各種プロセスやツールの集合がLinuxディストリビューションの実質的な構成要素
  • ディストリビューションが複雑になるほど不要な機能が積み重なり、**“bloated”**だと批判されることもある
  • 逆に、カスタムのマイクロディストリビューションを作れば、最小限の機能だけを含む軽量システムを構築できる

RISC-V向けLinuxカーネルのビルド

  • x86環境でクロスコンパイル用ツールチェーンを使ってRISC-V向けカーネルをビルド
    • kernel.orgからlinux-6.5.2.tar.xzソースをダウンロードし、make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- defconfigを実行
  • menuconfigでカーネル設定を視覚的に編集可能
  • make -j16で並列ビルドするとarch/riscv/boot/Imageが生成される
  • QEMUでqemu-system-riscv64 -machine virt -kernel arch/riscv/boot/Imageとしてブート
    • ブートログではSBIレイヤーの検出、UART初期化、printk有効化などのメッセージを確認できる

最初の障害: ルートファイルシステムがない

  • カーネルのブート中にVFS: Unable to mount root fsエラーでカーネルパニックが発生
    • 原因: ルートファイルシステム(initramfs)が提供されていない
  • ファイルシステムはディスクだけでなく**RAMベース(initramfs)**でも構成できる
  • initramfscpioフォーマットでパッケージされ、QEMUでは-initrdオプションでロードできる

initramfsを構築して“Hello world”を実行

  • 最小要件は/initバイナリの存在
    • init.cを書いて静的リンク(-static)でビルド
    • cpio -o -H newc < file_list.txt > initramfs.cpioでパッケージ化
  • QEMU実行時に“Hello world”を出力した後、initが終了するため再びカーネルパニックが発生
    • 解決策: initが終了しないよう無限ループを追加

Goで書いた簡単なシェルを追加

  • initforkexeclを使って/little_shellを実行
  • little_shell.goはユーザー入力を受け取り、コマンドをそのままエコー出力する単純なシェル
    • GOOS=linux GOARCH=riscv64 go build little_shell.goでRISC-V向けにビルド
  • initlittle_shellはどちらもUART経由で出力を共有
    • 標準入出力はファイルハンドルとして管理され、fork時に継承される
  • 結果として“Hello from init”とシェル入力が交互に出力される基本的なLinux環境が完成

カーネルの役割の整理

  • ハードウェア抽象化: ユーザープログラムはUARTやデバイスの詳細を知らなくても出力できる
  • 高水準インターフェースの提供: ファイルシステムを通じて別のバイナリ(little_shell)にアクセス
  • プロセス分離: initとシェルは独立したメモリ空間で実行される
  • カーネルは複雑なハードウェアの上で安定して移植性の高い実行基盤を提供する

オペレーティングシステムの定義

  • カーネルだけをオペレーティングシステムと見ることもあれば、ディストリビューション全体をオペレーティングシステムと見ることもある
  • 重要なのは、カーネルとユーザー空間の役割の境界と相互作用の構造を理解すること

u-rootで実際に役立つマイクロディストリビューションを作る

  • u-rootプロジェクトGoベースのユーザー空間ツールセットを提供
    • u-rootにはLinuxカーネル上で動作するユーザー空間ブートローダーとシェル環境が含まれる
  • インストール後、GOOS=linux GOARCH=riscv64 u-rootコマンドでinitramfsを自動生成
    • /tmp/initramfs.linux_riscv64.cpioファイルをQEMUで実行できる
  • ブート時には“Welcome to u-root!”バナーとともに基本シェルプロンプトが提供される
    • ls, pwd, echoなどの基本コマンドをサポートし、タブ補完機能も含む

ネットワーク接続の実習

  • QEMUにvirtio-net-devicevirtio-rng-pciデバイスを追加
    • -device virtio-net-device,netdev=usernet -netdev user,id=usernetオプションを使用
  • u-rootdhclientでDHCPによりIPを自動割り当て
    • 例: eth010.0.2.15/24が割り当てられる
  • wget http://google.com外部ネットワークへのアクセスに成功し、index.htmlのダウンロードを確認

パッケージマネージャーとinitの重要性

  • 一般的なディストリビューションはパッケージマネージャーを通じてソフトウェアを動的にインストール・更新する
    • この実習は組み込み型のアプローチであり、イメージ全体を再ビルドする必要がある
  • initは単なるプロセス起動器ではなく、デバイス初期化、サービス管理、システムブート制御の中核要素
    • u-rootinitソースコードを通じて、さまざまなデバイス(/dev)設定の過程を確認できる

GitHubリポジトリ

  • 本ガイドの完全なコードとサンプルはpopovicu/linux-micro-distroで提供
    • initramfsイメージのビルドと実習の再現が可能

1件のコメント

 
GN⁺ 2025-10-27
Hacker Newsのコメント
  • 数か月にわたって マイクロ Linux ディストリビューション を自作している
    ユーザーモードは単一の静的バイナリ1つで構成されていて、confidential microVM コンテナをサポートするためのいくつかのファイルだけがある
    特に initramfs の構造が興味深い
    カーネルが cpio アーカイブを展開して tmpfs に入り、/init を実行する過程はまるで魔法のようだ
    複数の cpio アーカイブを連結することもでき、それぞれ圧縮可能で、順番にオーバーレイされる
    このシンプルでありながらエレガントな設計のおかげで、自分でアンパックコードを書きながら多くを学んだ

  • 最近では qemu が主要アーキテクチャで uftrace をサポートし始めた
    専門家たちが「これをどうやってデバッグするんだ?」と尋ねるとき、そのまま答えになる
    関連内容は このスレッド を参照できる

  • 自分も似たようなプロジェクトを進めている — azathos
    自作の toy init、shell、そしていくつかのユーティリティを含んでいる
    デバッグ用に GNU coreutils を入れていて、今はフレームバッファに ウィンドウを描画する機能 に集中している

  • このプロジェクトは本当に素晴らしい
    98年にフロッピー ベースの「ディストリビューション」を作って、Windows PC のイメージングを UDP ブロードキャストでやっていた時代を思い出す
    「make bzimage」、init スクリプトのエラー、無限リブート…思い出が多い
    今のやり方もそれほど変わっていないのが興味深い
    Raspberry Pi 向けに移植したら面白くて教育的だと思う
    自分でも試してみるかもしれない

    • 自分も 98 年に Mandrake Linux を NetBIOS と ISDN でインストールしようとして、チェックサムエラー のせいで何十回もやり直した記憶がある
      結局、友人が sftp の内容を CD に焼いてくれて解決したが、そのときは 2 倍速でしか書き込めなかった
  • これを クラウドイメージ として(例: Vultr, DigitalOcean)動かしたり、GUI を立ち上げて Firefox を動かしたりするのがどれくらい難しいのか気になる

    • クラウドイメージとして動かすのは比較的簡単だ
      カーネルの基本ドライバさえあればよく、イメージをインストールすればいい
      別のディストリビューションで起動してから kexec で自分のカーネルを実行し、メモリ上でインストールする方法も可能だ
      実装例としては nixos-anywhere を参考にできる
    • ネットワークとストレージ用の virtio ドライバ を含むイメージを作り、qcow2 に変換して DigitalOcean などに登録すればいい
      思ったより簡単な作業だ
  • このプロジェクトを Raspberry Pi 向けに作ったバージョンがあれば本当に興味深いと思う

  • なぜこういうものを自作するのか気になったが、ただ Gentoo で Linux を探究するだけではだめなのかとも思った

    • Gentoo は「ソースからビルド」するが、パッケージマネージャがほとんどの作業を代わりにやってくれる
      ユーザー空間のカスタマイズは可能だが、Linux 自体を学ぶにはあまり向いていない
      stage3 tarball を見るだけでも、すでに「ミニディストリビューション」級だ
  • 学習用としては本当に良く、素早く完成させたいなら buildroot が良い選択肢だ

  • この記事のおかげで本当に多くを学んだ
    情報量の豊富なポスト に感謝する