Linuxについての理解を深める近道は、Linux上で動作するすべてのサービスが初期化される「起動シーケンス(ブートシーケンス)」を把握することです。Wind River Linuxを使って作成したLinuxディストリビューションの起動シーケンスをロギングし、内容を追いかけることにより、(1) 主要なサービスにはどのようなものがあるか、(2) どの機能とどの機能が連携しているか、(3) 自社に最適なLinuxディストリビューションを作成する上で不要なサービスはどれか、より最適なフットプリントのLinuxディストリビューションを作成にはどうすればよいかを解析可能です。そこで、本記事では、Wind River Linuxを題材に、Linuxを構成するコンポーネントを解説し、次に起動シーケンスをロギングする方法を説明します。
Linuxを構成するコンポーネント
まず、Linuxの起動シーケンスの内容から、Linuxにはどのようなコンポーネントが含まれているのかをご紹介します。
図1:Linuxを構成するコンポーネントと起動順序
1st-ブートローダ(primary boot loader)
1st-ブートローダはCPUに電源が投入された直後、最初に実行されるプログラムです。1st-ブートローダはリセット直後のCPUを初期化し、プログラムで固定されたストレージまたはネットワークデバイスへのアクセスを行い、ストレージに格納された、あるいはネットワーク上に公開されている2nd-ブートローダと呼ばれるプログラムをメモリ上へ展開する役目を担います。1st-ブートローダは固定的なアクセスのみの機能を備えた非常に小さなプログラムです。例えば、Raspberry Pi 4環境の1st-ブートローダは基板上のROMに格納されています。この1st-ブートローダはmicro-SDカード上の先頭パーティションにあるkernel*.img(2nd-ブートローダ)をメモリ上へ読み出し、実行を開始します。
ROMを更新することにより、1st-ブートローダの挙動が変化します。例えば、Raspberry Pi 4はボードのリビジョン毎に種類の異なる1st-ブートローダがインストールされています。Wind River Linuxに対応した1st-ブートローダを書き込むことによりWind River LinuxをRaspberry Pi 4上で起動できるようになります。
2nd-ブートローダ(secondary boot loader)
2nd-ブートローダはLinuxカーネルを実行するための準備をするプログラムです。ストレージを初期化する機能のみを備えた1st-ブートローダよりも高機能かつ大きなプログラムで、様々なパーティションタイプをサポートしています。Raspberry Pi 4の2nd-ブートローダは、Raspberry Pi 4起動用のmicro-SDカードの先頭パーティション(sdX1:FAT32フォーマット)に格納されたkernel*.imgに該当します。2nd-ブートローダは、Linuxカーネルを実行するために必要なハードウェアの初期化を行い、2番目のパーティション(sdX2:ext4フォーマット等)にあるLinuxカーネルの実行に必要なファイルを展開し、Linuxカーネルを開始します。Linuxカーネルは最初にstart_kernel関数が実行されます。
Linuxカーネル
Linuxカーネルは、起動時と定常時で行う処理が異なります。起動時には初期化処理を担当し、定常時にはリソース管理やイベント管理処理を担当します。
2nd-ブートローダからstart_kernel関数が呼び出される契機が、Linuxカーネルの開始タイミングです。Linuxカーネルはデバイスツリー情報(後述)を参照し、デバイスツリー情報に含まれているすべてのCPUやメモリのリソース管理を行ったのちに、各ハードウェアに対応するデバイスドライバをメモリにロードし、アプリケーションからハードウェアを操作するための準備をします。Raspberry Pi 4の場合は、デバイスツリー情報に、USBポート、Bluetooth機能、ネットワークインタフェース、電源管理機能をはじめ、USBポートに接続された各種USBデバイスの初期化がLinuxカーネルによって行われます。その後、ルートファイルシステムが格納されているストレージへアクセスし、/sbin/initスクリプトを実行します。/sbin/initスクリプトは、Linuxシステム全体を管理するsystemdを開始します(図1)。
起動完了後、定常状態となったLinuxカーネルは、デバイスからの割り込みや、アプリケーションからのシステムコールなどに応じて、各機能を中継、連携させる役割を担います。
デバイスツリー情報(dtbファイル、dtsファイル)
Linuxは、ハードウェアに依存しない基本的な制御ポリシーを定めるソースコードと、ハードウェア固有の制御方法を定義するソースコードから構成されています。ハードウェア依存のソースコードは、システム構成により利用される場合とされない場合があるため(例:Raspberry Pi 4のハードウェア制御方法とZynqのハードウェア制御方法は違う)、Linuxはハードウェア依存部と非依存部を分離できる設計となっています。ここで、ハードウェア依存の情報を「デバイスツリー情報」として管理し、デバイスツリー情報に列挙されたハードウェアを制御するためのハードウェア依存のソースコードが「デバイスドライバ」です。Raspberry Pi 4の場合はmicro-SDカードの先頭パーティション(sdX1)に格納されたCPU名(bcm2711)に続くdtbファイルに、バイナリ形式のデバイスツリー情報が格納されています。このデバイスツリー情報は、Wind River LinuxがLinuxディストリビューションを作成する際に、dtsファイルとして必要な情報のソースコードがダウンロードされ、bitbakeによりビルドされます。
デバイスドライバ
Linuxカーネルは、デバイスツリー情報により特定したハードウェアの構成に基づき、各ハードウェアを制御するためのデバイスドライバを呼び出します。デバイスドライバには、Linuxカーネルに組み込まれているものと、ファイルシステムにカーネルモジュールとして保存されているものがあります。Linuxカーネルはデバイスの利用開始・終了をopen、closeインタフェースとしてアプリケーションへ提供します。デバイスドライバのopen、close処理の実体は、Linuxカーネルがデバイスドライバを呼び出した際に結合します。その後、アプリケーションとデバイスドライバはread、write、ioctl等のシステムコールにより連携します。デバイスドライバがアプリケーションへ提供するインタフェースは、デバイスのタイプ(ブロックデバイス、キャラクタデバイス、など)毎に最適なものを選択できるようになっています。
systemd
systemdは事前に登録された順序に従い、Linuxディストリビューションを構成するコンポーネントや、ユーザモードのサブモジュール(サービス)を起動します。ここで起動されるものには、ネットワークの名前解決を担当するnamed機能や、ネットワークインタフェースに割り当てるIPアドレスを解決するDHCPクライアント、パケットの送受信設定に従いネットワークを制御するiptables機能などがあります。
journald
journaldはsystemdを構成するひとつの機能であり、systemdが実行した処理をすべてロギングします。journaldコマンドを実行することにより、systemdの動作履歴を取得することが可能です。
シェルプロンプトとX Window System
systemdは、ユーザに必要なすべての機能を初期化した後、login機能を起動します。login機能はユーザ名とパスワードを確認し、シェルプロンプトを開始するための機能です。これにより、Linuxのインタラクティブな操作が可能となります。グラフィカルモードの場合はログイン後、X Window System機能がユーザにGUIを提供します。
起動シーケンスのログを取得する
Linuxの起動シーケンスをロギングする方法には、Linuxの標準機能である"dmesg"や、systemdの標準機能である"journald"、起動シーケンスをグラフィカルに記録するsystemdの拡張機能"BootChart"などがあります。ここでは、Wind River Linuxをビルドして、BootChartのログと、dmesgのログ、journaldのログを取得する方法を紹介します。
動画:組込みLinuxの起動シーケンスを理解する
起動ログを取得できるLinuxディストリビューションを
ソースコードからビルドする
事前準備
Wind River Linuxを使用するには、LinuxをインストールしたホストPCが必要です。推奨されるホストPCの要件は「Wind River Linux Release Notes:Host System Recommendations and Requirements」をご確認ください。また、「Necessary Linux Host System Libraries and Executables」に掲載されているパッケージも事前にインストールしてください。今回の動画ではホストPCにUbuntu Desktop 22.04(x86 64bit)を選択しています。
Wind River Linux LTS22のLinuxディストリビューションのソースコード入手
「Wind River Linux Platform Development Quick Start, LTS 22」を参考に「Wind River Linux GitHub」を開きます。GitHubのURLがWEBサイト上に表示されます。このURLにアクセスするとGitHubページが開きますので、gitコマンドに指定するclone用のURLを取得してください(右図)。
clone用URLと、Wind River Linux LTS22を示すブランチ名"WRLINUX_10_22_BASE"を付与した、git cloneコマンドを実行し、開発環境を構築します。以下は、ホームディレクトリの中に作業用ディレクトリ"bootlog"を作成し、bootlog内に開発環境を構築する例です。
ホストPC上での操作
$ mkdir ./bootlog
$ cd ./bootlog
$ git clone --branch WRLINUX_10_22_BASE \
https://github.com/WindRiverLinux22/wrlinux-x.git
図2:GitHub上のWind River Linux
Linuxディストリビューションのビルド
次に、BootChart機能を備えたLinuxディストリビューションを作成するための設定を含むビルドシステムを取得します。git cloneコマンドにより入手した"./wrlinux-x/setup.sh"開発ツールに次のオプションを付与して実行することで、ビルドシステムを取得できます。
ホストPC上での操作
$ ./wrlinux-x/setup.sh --list-machines
<利用可能なBSPの一覧が表示される>
$ ./wrlinux-x/setup.sh --machines bcm-2xxx-rpi4
<Raspberry Pi 4向けのLinuxディストリビューションをビルドする準備を行う>
続いて、". ./environment-setup-x86_64-wrlinuxsdk-linux"コマンドを実行してください。このコマンドにより、開発作業に必要なPATHがシステムに追加されます。続いて". ./oe-init-build-env"のコマンドを実行すると、Linuxディストリビューションをカスタマイズするための設定ファイルを含む、ビルド用ディレクトリが作成されます。
ホストPC上での操作
$ . ./environment-setup-x86_64-wrlinuxsdk-linux
$ . ./oe-init-build-env
"conf/local.conf"を編集して、ビルドの設定を行います。ビルド中にネットワークからパッケージを自動収集できるよう"BB_NO_NETWORK ?= '0'"を指定します。BootChart機能を備えたLinuxディストリビューションを生成するために"IMAGE_INSTALL:append = " system-bootchart""を追記します。
ホストPC上での操作
$ vi conf/local.conf
BB_NO_NETWORK ?= '0'
IMAGE_INSTALL:append = " systemd-bootchart"
最後に、Linuxディストリビューションをビルドします。ビルドには"bitbake"コマンドを利用します。bitbakeコマンドの引数には、標準のビルドを意味する"wrlinux-image-std"を指定し、オプションとしてアプリケーションを開発するためのSDKを生成する"-c populate_sdk"を指定します。完了までしばらくお待ちください。
ホストPC上での操作
$ bitbake wrlinux-image-std -c populate_sdk_ext
Wind River Linuxを起動して起動シーケンスのログを取得する
ビルドしたLinuxディストリビューションをQEMU上で実行し、起動シーケンスのログを取得してみましょう。実行には"runqemu"コマンドを使います。runqemuコマンドによりQEMUの実行が開始されると、CUIのWind River Linuxが起動します。
ホストPC上での操作
$ runqemu bcm-2xxx-rpi4 nographic
起動後、ログを取得します。次のコマンドを実行することにより、ログを取得することができます。
Wind River Linux上での操作
# scp /run/log/bootchart-*.svg ホストPCのユーザ名@ホストPCのIPアドレス:~/
※bootchartのファイル名は起動日時によって変化する
# dmesg > dmesg.txt
# journalctl > journald.txt
# scp *.txt ホストPCのユーザ名@ホストPCのIPアドレス:~/
Linux起動シーケンスの内容を確認する
前手順にて取得したBootChartはSVG形式となっており、起動シーケンスの内容をグラフィカルに解析可能です。BootChartファイルの先頭部分には、発生したI/O使用率(ReadおよびWrite)とCPU使用率が記載されています。BootChartファイルのメイン部分には、プロセスの起動タイミングと、プロセスID、処理時間がグラフで記載されています。以下が、動画内で取得したBootChartファイルです。
動画内で取得した起動ログ(QEMU:Raspberry Pi 4、BootChart、dmesg、journald)
図3:BootChartファイル
本記事では、Linuxを構成するコンポーネントと起動シーケンスについて解説しました。Yocto ProjectベースのWind River Linux を使うことにより、簡単に価値のあるコンポーネントを追加したり、不要なコンポーネントを削除することが可能です。自社向けに作成したLinuxディストリビューションの最適化に、是非本記事をお役立てください!