Skip to content

Instantly share code, notes, and snippets.

@splhack
Last active August 29, 2015 14:06
Show Gist options
  • Save splhack/48c5608ba7e1cf6884d6 to your computer and use it in GitHub Desktop.
Save splhack/48c5608ba7e1cf6884d6 to your computer and use it in GitHub Desktop.
カーネル、私の読み方
坂本一樹 sakamoto@netbsd.org
始まりは「仕事」、でも...
なぜカーネルのソースコードを読んだり書いたりすることになったのかと
問われれば、それは「仕事」だったから、と言える。そもC言語による
プログラミングをはじめたのも、FreeBSDやNetBSDを使いはじめたのも、
「仕事」で使うからという理由である。しかしMIPS R3000を使った
UNIXマシンのOS保守やデバイスドライバの作成などの業務に携わっているうちに
(というよりもUNIXマニアのK上司にあてられて、という表現の方が正しい:-)
かもしれないが)、すっかりUNIX好きになり、しまいには
マイナーなマシンにNetBSDを移植してしまった。これは完全に趣味である。
UNIXとC言語
前述のような業務をこなすためには、C言語の技術は必須であったわけで、
仕事道具としてC言語を身に付けるべく次の書籍で勉強することになった。
・プログラミング言語C 共立出版 ISBN4-320-02692-6
・UNIXプログラミング環境 アスキー ISBN4-87148-351-7
・Cパズルブック カットシステム ISBN4-87783-029-4
カバーの取れたぼろぼろのK&R本(プログラミング言語Cのこと)を
「一日でできるよね :-)」と手渡されたおかげで、
今の自分があると言っても過言ではない。それはともかく、
C言語の知識不足がカーネルを読む際の障壁になることもある。
是非C言語を道具として使えるようになって欲しい。
当時は出版されていなかったが次の書籍は大変オススメである。
・プログラミングの力を生み出す本 オーム社 ISBN4-274-13207-2
・エキスパートCプログラミング アスキー ISBN4-7561-1639-6
まずはデバイスドライバ
C言語の勉強といっても本だけ読んでいるのは大変つまらない。
実践あるのみ、とISAバス接続の簡単なI/Oボードを制御するプログラムを
作成する機会が与えられた。DOS環境でなんとか作成することができたものの
FreeBSD(当時は2.0.5 RELEASE)用も作成することになったとたん困った。
さわりはじめたばかりではあるが通常さわるFreeBSD環境
つまりユーザーランドでは、ISAバスの先を叩くことができない
(本当はできる。当時はユーザーランドから
I/Oアクセスのための特権を得る方法に気付かなかった)、と。
だが、これによりカーネル内部と外部の違いを勉強する機会と
デバイスドライバを作成する機会を得られたわけであるから幸運である。
ここでは次の書籍とFreeBSDのドキュメントを使って勉強した。
・UNIX 4.3BSDの設計と実装 丸善 絶版
・UNIXカーネルの設計 共立出版 ISBN-4-320-02551-2
カーネルとデバイスドライバとユーザーランドの仕組み、
通常ファイルとデバイスファイルの違い、デバイスファイルの作成方法、
カーネルソースコードの入手方法、デバイスドライバの場所、
デバイスドライバの追加方法、カーネルのコンパイルの仕方、
カーネルの入れ替え方、デバッグ用の文字列出力、デバッグ出力先、
などの壁を乗り越え、
/usr/src/sys/i386/isa 以下を眺めながら見よう見まねで
list1のようなスケルトンソースコードを作り、
DOS環境のプログラムから切り張りしていった。
----list1-----------------------------------------------------
struct hogesoftc {
struct device sc_dev;
...
};
struct cfdriver hogecd = {
NULL, "hoge", hogeprobe, hogeattach, DV_TTY, sizeof(struct hogesoftc)
};
int
hogeprobe(parent, self, aux)
struct device *parent, *self;
void *aux;
{
...
}
void hogeattach(parent, self, aux)
struct device *parent, *self;
void *aux;
{
...
}
int
hogeopen(dev, flag)
dev_t dev;
int flag;
{
...
}
int
hogeclose(dev, flag)
dev_t dev;
int flag;
{
...
}
int
hogeread(dev, uid)
dev_t dev;
struct uio *uio;
{
...
}
int
hogewrite(dev, uio)
dev_t dev;
struct uio *uio;
{
...
}
int
hogeioctl(dev, cmd, addr)
dev_t dev;
int cmd;
char *addr;
{
...
}
--------------------------------------------------------------
ユーザーランドからこのデバイスドライバ用の/dev/hoge0に
アクセスできたときは、それはもう感動した。本に書いてあるように
カーネルが動いたのが実感できたわけであるから。
このように、C言語を道具として使えるほど身に付いていない状態でも
カーネルをいじることができる。勉強の題材としても使えるわけだ。
No.16で前川氏も書かれているが、
C言語で「printfで画面に文字が表示できる」プログラムを
コンパイルして実行できる人であれば、カーネル内部に自分のプログラムを
埋め込んでコンパイルし、そのカーネルで起動することも簡単だ。
カーネルをハックしたいがどこをハックしたら良いか思い付かない、
という人はカーネル起動メッセージを書き換えて「マイBSD」を作ってみよう。
デバイスドライバやシステムコールのような所をハックするなら、
ユーザーランドからも遊べて一石二鳥だ。
コンパイルできなかったり、起動できなくても心配ない。
カーネルのどこをどういじろうが他人に迷惑をかけることはない
(注: 万が一のためネットワークからは切り離しておこう。)。
こころゆくまでカーネルをハックして欲しい。
カーネルハックには、BSD magazineバックナンバー(No.4の特集1がオススメだ)や、
FreeBSD、NetBSD、OpenBSDなどのサイトが参考になると思う。
また次の書籍も勉強になる。
・386BSD カーネルソースコードの秘密 アスキー ISBN4-7561-2042-3
英語に抵抗なければ次の書籍も入手したい。
・The Design and Implementation of the 4.4BSD Operating System
Addison Wesley Professional ISBN0-201-54979-4
デバイスドライバの次は
と、まぁここまで来てしまうと自分の持っているマシンで
BSDを動かしてみたくなるもので、というか、
わざわざBSDが動いていないマシンを買ってみた。
Be,Inc.のBeBoxである。BeOSという新しいOSや、
PowerPCが2個載っているマルチプロセッサマシンというのも購入動機であったし、
沖氏(注: まさか同じ特集記事を書く日が来るとは。)が
NetBSDを移植されそうでいて移植されていない、という点も購入の決め手となった。
CPU以外はIBM PC/AT互換機と言えるし、NetBSDがPowerPCに対応している、
自分でも動かすことができるのではないか、と。
(注: BeBox以外はことごとく失敗または途中で飽きてしまっているが、
BeBox以降「NetBSDが動きそうだけどまだ動いてない」というモノを
買ってしまう癖がついてしまった。
Geofox、PRESARIO213、PocketPostPet、Dreamcast、...)
カーネルの移植にあたり最も重要なことは、
ターゲットマシンのCPUをすべてのっとることができるかどうか、である。
BeBoxではすでにLinuxPPCが動作していたので、その点は心配なかった。
のっとるためのプログラムはbinutils、gccでクロスコンパイルできる
と思って間違いないだろうが、ここができないとなると
コンパイラを探す、移植するところからはじめなければならない。
ターゲットプログラムをコンパイルできたならば、
それを起動させる方法を見つける必要がある。BeBoxでは
binutils、gcc、LinuxPPCのブートプログラムを利用させてもらった
(カーネルが起動するようになってから、
Be,Inc.がBeBoxでOSを起動させるためのドキュメントを公開していたことに
気付いたと言う後日談がある。情報収集は怠らない方が良いだろう)。
物理的にアクセスできるモノであれば、
思いきって改造してしまったり、実行ファイルを特定のファイルパスに
おくだけで良かったり、OSやアプリケーションの「穴」を利用したりといった、
何かしらの方法でCPUをのっとれるのではないだろうか。
CPUをのっとる、つまり自分の作ったブートプログラムを特権レベルで
動作させることができれば、次はカーネルに制御をうつす方法を考える。
PCのBIOSに相当する機能が提供されていれば、カーネルをメモリ上に置くことは
造作もないだろうし、CPUから先のデバイスアクセス方法がわかれば、
カーネルをメモリに読み込むブートプログラムを作ることが可能だろう。
BeBoxではBIOSに相当するものが入っていたが、
内容が公開されていなかったため、フロッピーディスクドライブの
デバイスドライバを作成し、フロッピーディスクからカーネルを起動させた。
移植作業途中でブートプログラム自身にカーネルをくっつけるという
アイディアを見つけてからは、もっぱらその方法でカーネルを起動させている。
以上のようにCPUをのっとる方法やブートプログラムの作り方は
ターゲットに依存するため、ターゲット毎に試行錯誤することになると思うが、
ブートプログラム自体(CPUやデバイスを初期化してC言語のプログラムに
制御をうつす処理など)に必要なアセンブラや、
ブートプログラムのファイル形式やメモリ上の配置などの
基礎知識としてはターゲットCPU、チップセットのマニュアルや
次の書籍を読むと良いだろう。
・コンピュータの構成と設計(上) 日経BP ISBN4-8222-8056-X
(下) ISBN4-8222-8057-8
・Linkers & Loaders オーム社 ISBN4-274-06437-9
カーネル移植
カーネルが起動してしまえばこっちのもの。あとは動くように
カーネルを変更していくだけだ。第一目標はprintf出力を自分の目で拝むこと。
RS232などのシリアルや、イーサネットなどのネットワークデバイス、
ビデオコンソールデバイスなどのデバイスドライバを作成することに
なるだろう(そこまでいかずgetc、putcなどでいい場合もある)。
BeBoxではPowerPCの先にIBM PC/AT互換機がぶらさがっているという
アーキテクチャ構造そのままに、NetBSD/powerpc(現在はofppc)にNetBSD/i386
を繋げていくという手法を取った。printfはあっという間に出力された。
NetBSDのソースさまさまである。
次の目標はシングルユーザーモードで起動することだろうか。
筆者はここで多くの時間を費やした。NetBSDはマシン非依存(MI)部、
マシン依存(MD)部に大きく分けることができるが、カーネル移植中に
マシン非依存部で困ったことが起きた場合は、間違いなくマシン依存部の、
しかも自分が書き換えた場所が原因である、と言うのは言い過ぎだろうか。
一番はまったところは、仮想メモリシステムの一部であるpmapの
マシン依存部である。ここがしっかりしていないと、カーネルが
あらゆるところで予想もしない落ち方をする。今では
BSD magazineのバックナンバーで仮想メモリシステムに関する記事を
読めるわけであるから、是非ともカーネルソースコードを読む際に利用して欲しい。
次に困ったことと言えば、
デバイスドライバやファイルシステムなど多くの場面で
表面化することとして、エンディアン(バイトオーダー)[図]に関する問題がある。
----図---------------------------------------------
0x12345678 というワードデータを
アドレス 0 1 2 3
+--+--+--+--+
|12|34|56|78|
+--+--+--+--+ と格納するのがビッグエンディアン
| | | |
| | | |
--+++-- エンディアンが異なる場合は
| | | | オーダーを変換する必要がある
v v v v
アドレス 0 1 2 3
+--+--+--+--+
|78|56|34|12|
+--+--+--+--+ と格納するのがリトルエンディアン
---------------------------------------------------
BeBoxのCPUはビッグエンディアンで固定されており(PowerPC自体は
バイトオーダーを変更できる)、CPU、チップセットから先は
PCIバス、ISAバスといったリトルエンディアンの世界が広がっている。
デバイスをコントロールする際はたとえばlist2のように
バイトオーダーを変換する必要があるが、
デバイスに読み書きするデータは変換しないなど、
かなり混乱させてもらった。
なお現在のNetBSD/powerpcのbus_space系関数はもっと洗練化されている。
----list2-----------------------------------------------------
/*
* bus space read function
*/
#define SWAP_16(p) ((u_int16_t)((p[1]<<8)|(p[0]<<0)))
#define SWAP_32(p) ((u_int32_t)((p[3]<<24)|(p[2]<<16)|(p[1]<<8)|(p[0]<<0)))
u_int16_t bus_space_read_2(tag, bsh, offset)
bus_space_tag_t tag;
bus_space_handle_t bsh;
bus_size_t offset;
{
u_int16_t val = *(u_int16_t *)(tag + bsh + offset);
u_int8_t *p = (u_int8_t *)&val;
return (SWAP_16(p));
}
u_int32_t bus_space_read_4(tag, bsh, offset)
bus_space_tag_t tag;
bus_space_handle_t bsh;
bus_size_t offset;
{
u_int32_t val = *(u_int32_t *)(tag + bsh + offset);
u_int8_t *p = (u_int8_t *)&val;
return (SWAP_32(p));
}
--------------------------------------------------------------
また、シングルユーザーモードで起動するということは、
カーネルからユーザーランドコードを実行するということである。
そこで最初に実行することになる /bin/sh を用意する方法も
考えなければならない。同一CPUアーキテクチャのユーザーランドバイナリが
ダウンロードできる状態であればそれを利用しよう。もしできなければ
カーネル用に使用しているクロスコンパイラで、ユーザーランドバイナリを
生成できるようにヘッダーやライブラリをそろえる必要があるだろう。
/bin/sh をどこに置いてカーネルに見せるか、という点も考える必要がある。
BeBoxでは、ファイルシステムでエンディアンに関する問題がでないように、
ISO9660ファイルシステムを使用してフロッピーディスク上に置いた。
ネットワークデバイスを使用してNFSファイルシステムを利用するのも良いだろう。
シングルユーザーモードで起動できるようになれば、
マルチユーザーモードで起動できるようにユーザーランド環境を
整えるべくプログラムをクロスコンパイルしていく作業をしたり、
より多くのデバイスドライバをそろえていく作業をすることになるだろう。
こうなると移植作業もほぼ終わり、
他のport同様メインテナンス作業にうつることになる。
と、こんな感じに
「NetBSDが動きそうだけどまだ動いてない」というモノを見つけた場合は、
BSD magazineバックナンバー片手に是非ともカーネル移植に挑戦してみて欲しい。
「FreeBSDが動きそうだけどまだ動いてない」だったり
「OpenBSDが動きそうだけどまだ動いてない」というモノも当然アリだ。
なお、カーネル移植に関するWebページは
Porting BSD UNIX to a New Platform
http://www.teamten.com/lawrence/291.paper/291.paper.html
などをはじめ結構(?)ある。探してみよう。
ソースコード管理
最後にカーネルソースコードをいじるのが大変楽になるものを紹介しておこう。
CVSだ。筆者はNetBSD/beboxをNetBSDのCVSリポジトリーに突っ込むことになるまで
使ったことがなかった。大変だった。新しいソースコードをダウンロードしては
展開し、そこに今までの変更分を上書き展開したり、
diffをつくってpatchをあてたり。自分の変更分と重なっていることに
気付かずに不可解なバグに悩んだこともあった。CVSに限らず、
ソースコードのバージョン管理とローカルでの変更を管理できるツールが
あれば、最新版カーネルをおいかけつつ移植作業を行うという芸当もできる。
是非とも利用して欲しい。
CVSを使ってカーネルソースコードをいじる場合は、
CVS infoの「サードパーティのソースの追っかけ」が参考になる。
http://www.sodan.org/~penny/vc/cvs-ja_13.html#SEC104
----図---------------------------------------------
NetBSD移植作業CVS枝例
main trunc NETBSD
| vendor branch
|
+-------------------------------------+
| |
移植作業 |
| |
| <--------マージ------------ DATE-2003-08-01-00-00
マージ作業 |
| |
移植作業 |
| |
| <--------マージ------------ DATE-2003-08-02-00-00
マージ作業 |
| |
移植作業 |
| |
. .
. .
. .
--------------------------------------------------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment