仮想環境を使い Linux Kernel 内で動作するプログラムを作っていきます。ユーザー・ランドで動作するプログラムとの違い。Linux Kernel の様子を探っていきます。ここでは Linux で動作する VirtualBox を使用します。Windows で動作する VirtualBox も利用できます。Windows で動作する VirtualBox では不便な場面は有るかと思いますが、なるべく困らないようにしていきたいと思います。
VirtualBox に次のような設定の仮想マシンを用意してください。他の仮想環境を使う場合は、よく似た環境にしてください。
構成要素 | 設定 | 説明 |
仮想マシンOS (Type) | Linux | この OS のデバイス・ドライバ開発の話ですので。 |
仮想マシンディストリビューション (Version) | Ubuntu ホストと同じ CPU 語長 | Ubuntuを使用します。Release を指定できる場合は 14.04 を指定してください。仮想マシンの CPU 語長はホスト環境と同じにする事を推奨します。ホストマシンと合わせる目的は、コンパイル速度が遅い場合、ホストマシンで補助しやすくするためです。 |
仮想マシンメモリ容量 (Base Memory) | 1024Mibyte 以上 | 遅いですが、セルフコンパイルを実施するためです。できれば 2048Mibyte 以上にしてください。 |
プロセッサ数(Processor(s)) | 2 個以上 | 2個を割り当てると可能な実験をする予定です。 |
リモート・ディスプレイ(Remote Display) | Disable | リモートディスプレイ機能は使用する予定が無いので disable で良いです。 |
光学ドライブ (optical drive) | 1 台 | Ubuntu インストーラー ISO イメージをマウントします。 |
ハード・ディスク (hard disk) | 1 台, 200G byte | Ubuntu、Linux Kernel ソース、それをコンパイルしたオブジェクトを格納します。容量の 100% まで使用する予定はないので動的確保(Dynamically Allocated)で構成してください。 |
ネットワーク・アダプタ接続 (Attached to) | bridged adapter | ブリッジ接続をおすすめします。ssh で入ることで状況を立て直せる場合もあります。ネットワークにコンピュータを追加できない場合は NAT, Internal Network, Host Only network を使い分けるか複数用意してください。 |
シリアル・ポート番号 (Port Number) | COM1 | もしかしたら使うかもしれません。 |
シリアル・ポート・モード (Port Mode) | Disconnected | もしかしたら使うかもしれません。 |
次は VirtualBox の仮想マシン設定一覧の画像です。赤線を引いたところは設定を必要とする項目です。
単純なパーティション構成で Ubuntu をインストールします。Installation Type にて Erase disk and install Ubuntu を選びます。
次の表のようにパーティションが分割されます。
パーティション | ファイル・システム | マウント・ポイント | 備考 |
/dev/sda1 | ext4 | / | 殆どこのパーティションに容量が割れ当てられます。 |
/dev/sda5 | swap | おおよそメモリ容量と同じ量が割り当てられます。 |
次はパーティション分割を終えた後のスクリーン・ショットです。
ノート
インストール後、仮想マシンの画面が狭く感じる場合は xorg.conf ファイルを root 権限で /etc/X11 ディレクトリにコピーしてください。logout/login 後に画面サイズは 1280x1024 になります。解像度を下げたい場合は、xorg.conf の Modes 行の解像度名(たとえば "1280x1024")を削除してください。
私の好みも含め、パッケージを追加しデバイス・ドライバ開発に備えます。apt-get install を目的別に書いたので、お手数をおかけします。
$ sudo apt-get install ssh $ sudo service ssh start $ ifconfig # IP アドレスを調べてリモートより操作する手掛かりを収集
$ sudo apt-get install sysv-rc-conf
$ sudo sysv-rc-conf --level 2345 ssh on
$ sudo apt-get install git gitk git-gui $ sudo git config --global user.name "Your Name" # Your Name の部分は英語で自分の名前にします。 $ sudo git config --global user.email "your.email@example.com" # your.email... の部分はメールアドレスら置き換えます。
$ sudo apt-get install libncurses-dev
ノート
git gui コマンドを使って GUI の git ツールを起動し、commit を行った人の名前、メールアドレスに加えスペルチェック機能も設定すると便利です。
$ sudo apt-get install grub2
$ sudo apt-get install nfs-kernel-server $ sudo service nfs-kernel-server on $ sudo sysv-rc-conf --level 2345 nfs-kernel-server on
ノート
設定ファイル /etc/exportfs を編集していないので NFS で共有するディレクトリはありません。 /etc/exportfs を編集した後、sudo service nfs-kernel-server reload で設定が有効になります。
$ sudo apt-get install samba winbind libnss-winbind $ sudo sysv-rc-conf --level 2345 nmb on $ sudo sysv-rc-conf --level 2345 smb on $ sudo sysv-rc-conf --level 2345 winbind on $ sudo nano /etc/samba/smb.conf # samba 設定内容を好みに合わせて編集 $ sudo nano /etc/nsswitch.conf # hosts: 行に有る dns の前に wins を追加 $ sudo service nmb start $ sudo service smb start $ sudo service winbind start
linux kernel のソースコードを取得します。ローカルの git リポジトリを置く場所を git_base_directory とします。ソース・コードを clone すると git_base_directory/linux-stable 以下に取得したファイルが配置されます。次に示す手順は、git_base_directory を作成することを前提としています。ホームディレクトリ直下に配置する場合は、git_base_directory を作成するコマンドは省略してもよいです。
$ mkdir -p git_base_directory $ cd git_base_directory $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git
ノート
git clone コマンドは完了するのに 2 時間 ~ 半日程度かかる場合があります。
Ubuntu 14.04 に使っている Linux Kernel よりは新しく、long term で更新されている kernel を選びます。これを書いている時点で 4.1.27 を選択しました。ローカルの git repository に 4.1.27-local branch を作成し作業を進めます。次のようにします。これより後、特に断りがなければ、先の節でソースコードが配置されたディレクトリ git_base_directory/linux-stable を単に linux-stable と表現します。
$ cd linux-stable $ git checkout v4.1.27 $ git branch v4.1.27-local $ git checkout v4.1.27-local
ノート
linux kernel はバージョンを上げても userland 向け API は変わらないように開発されています。ただし、現実的な問題として API は変わらずとも、大きな拡張があると拡張機能に対応した設定ツール群が無く操作に難儀することがあります。デバイス・ドライバのサポートが打ち切られたり、メンテナンスが不十分なため挙動が変わることもあります。
ノート
ここで branch を作成しても、本稿で積極的に topic branch (ちょっとした実験的な変更を追跡する branch)を作成するような操作を示さないかもしれません。branch を活用すれば、変更したコードをそのまま topic branch に commit した後、変更前の branch や tag を checkout して、綺麗さっぱりやり直すこともできます。手で修正を戻したり、#if defined(COND) .. #endif で括りながら修正を試すより、大胆にコード修正に取り組めるはずです。 Linux Kernel のソースコードを読むと、おそらく普通の開発で見る #if 0 .. #endif の様なコードは殆ど見ることは無いはずです。デバックや様々な実験は topic branch 上で行い。納得がいったら、綺麗にして本線へ merge するか、修正を施すように流れで開発されています。
ソース・コード・ツリーの根にある .config ファイルの内容を make や gcc のマクロ定義値として読み込み、Linux Kernel のソース・コードはコンパイルされます。.config ファイルに書かれた値を全て吟味していると、内容を把握していても数時間を要するでしょう。いずれ .config ファイルの内容のうちデバイス・ドライバ開発で興味深い部分に触れる予定です。今は、既にある .config ファイルを使ってソース・コードをコンパイルします。
$ cd linux-stable # 前節の続きであれば不要です。 $ cp /boot/config-`uname -r` ./.config $ make menuconfig
良くあるエラー
In file included from scripts/kconfig/mconf.c:23:0:
scripts/kconfig/lxdialog/dialog.h:38:20: fatal error: curses.h: No such file or directory
上のようなエラーが出た場合は、パッケージを追加してください。
$ sudo apt-get install libncurses-dev
ノート
uname -rで「今の」kernel version を出力します。上手く .config ファイルを流用するにはなるべく近い version で古いか同一のものを使います。
ノート
menuconfig を使う理由の一つは、古い Kernel に無かった新しい機能についてデフォルト値を決めていない設定を確定させるためです。ソース・ツリーの各ディレクトリに Kconfig というファイルがあり、この中にデフォルト値が書かれています。
ノート
クロス開発では .config ファイルをどうやって手に入れるの? arch/processor/configs/platform_defconfig に格納されています。これを $ make platform_defconfig で導入します。SoC チップ向けの platform_defconfig ファイルにはチップに備えられた周辺機能をおおよそ上手く動かすのに必要な設定が書かれています。
make menuconfigを実行すると次のような画面になります。すぐにコンパイルを始められるよう最低限の設定をして先に進みます。画面のスナップショットを全て示すのは大変なので、選択項目と入力内容を示しながら進めます。例えば "General setup" と書いた場合、次の画面の下線部分をカーソルキーで選択し [Enter] を押して選択することを意味します。
.config - Linux/x86 4.1.27 Kernel Configuration
──────────────────────────────────────────────
┌── Linux/x86 4.1.27 Kernel Configuration ──────────────────────┐
│ Arrow keys navigate the menu. <Enter> selects submenus ---> (or empty submenus │
│ ----). Highlighted letters are hotkeys. Pressing <Y> includes, <N> excludes, <M> │
│ modularizes features. Press <Esc><Esc> to exit, <?> for Help, </> for Search. │
│ Legend: [*] built-in [ ] excluded <M> module < > module capable │
│ ┌────────────────────────────────────────┐ │
│ │ [*] 64-bit kernel │ │
│ │ General setup ---> │ │
│ │ [*] Enable loadable module support ---> │ │
│ │ [*] Enable the block layer ---> │ │
│ │ Processor type and features ---> │ │
│ │ Power management and ACPI options ---> │ │
│ │ Bus options (PCI etc.) ---> │ │
│ │ Executable file formats / Emulations ---> │ │
│ │ [*] Networking support ---> │ │
│ │ Device Drivers ---> │ │
│ │ Firmware Drivers ---> │ │
│ │ File systems ---> │ │
│ │ Kernel hacking ---> │ │
│ │ Security options ---> │ │
│ │ -*- Cryptographic API ---> │ │
│ │ -*- Virtualization ---> │ │
│ │ Library routines ---> │ │
│ │ │ │
│ └────────────────────────────────────────┘ │
├────────────────────────────────────────────┤
│ <Select> < Exit > < Help > < Save > < Load > │
└────────────────────────────────────────────┘
menuconfig を終えると linux-stable ディレクトリに .config と .config.old ファイルが出来上がります。差分を観察してみると、次の点に気づくでしょう。
menuconfig を使用すると、設定を一覧でき修正も楽です。しかし、変更点の追跡は困難になります。2, 3 項目の修正であれば .config ファイルを直接修正して開発を進めるのが良いでしょう。あるいは menuconfig を使って生成した .config ファイルを参照しつつ、リポジトリ管理している .config ファイルを修正することになります。
$ make -j 4