Linux Kernel Driver Quest †本ページの Linux kernel version は linux-4.1.27 です。 はじめに †「自分が Linux kernel driver を書いてきた経験を整理したい」という目的でこのページを書く予定です。既に世の中には数多くの Linux kernel driver を書くための参考書があります。このページがこれらの参考書より優れた記述になる自信はありません。Linux kernel driver を書こうとするとき、様々な疑問や気になることをフラフラしながら書いていく予定です。 driver を書く上で知っていれば良い範囲で Linux kernel を読んでいきます。多くの kernel 解説書で触れているような 起動処理、排他制御、リソース管理(メモリ、ディスク、他)、プロセス・スレッド管理/スケジューリング、ファイルシステム等は軽く触れるだけにします。 このページの進め方 †全体を通して ソースコードを追跡できる環境 を使って Linux kernel を読み進め、理解を深めるやり方で進めます。ソースコードを追跡する環境は web page として用意します。説明の各所でソースコード中のシンボルとそれに関連するソースコートを読めるリンクを張ります。一々リンクを辿るのは面倒かもしれません。それでも、ソースコードを読んでいくことを強く希望します。自分は Linux kernel はソース自体が仕様書だと思っています。1 行、1 行、あるいはもっと細かく 1 演算子に相当する処理であっても、なぜその順で処理していくのか。いくつかの計算方法から、なぜその方法を選んだのか意図が込められている箇所があります。人間の言葉では書ききれない仕様です。 使用する書式・表現 †文字の字体や記号、段落の背景色を使い文書の意図や背景を表現する箇所があります。記述が進むにつれ、表現は追加され、継続して読むのに問題が起きない範囲で若干の修正を加えるかもしれません。 文字装飾 †字体と記号を使って、単語や文字列の由来や表現する内容を次のようにします。
囲み記事 †本文の流れに付け加えるような参考になる情報、注意を要する事、深刻な問題を起こすような事項は囲みを使って表現します。表現内容が短い場合は、背景色だけ違う段落を用いて表現します。 このページの背景 ページ全体、あるいはその一部の背景を説明します。表示しているソースやシンボルのリンク先 kernel version を示すことが多いです。 ノート 付け加えて参考になる情報を緑色の囲みで書きます。応用できる事項、関連する事項、詳細や背景を説明します。 注意 注意が必要な情報を黄色の囲みで書きます。予測に反して起きる事柄、応用や修正が思ったほどには上手く行かない場合、深くソースコードの読解や挙動の把握が必要な場合を説明します。 警告 ほぼ禁止事項に関する内容を赤色の囲みで書きます。復旧困難な問題、追跡・解析が難しい問題、動作に不具合を起こす問題など、深刻な問題について説明します。 ちょっとした考え事 自分が考えている事について書きます。解決したところで大きく何かが変わるわけでもない疑問、Linux kernel 内のしきたりから外れていること、広く一般的に同意が得られないことについて書きます。 期待している経験値 †手短に言えば linux あるいは POSIX 相当環境で、C 言語によるアプリケーション開発経験があると期待しています。最低限 2,000 行程度のアプリケーションを 4, 5 本は書いたという程度です。man page section 2 にある system call を使った経験があるのが望ましいです。たとえば open() (kernel 内 SYSCALL_DEFINE3 open), read() (kernel 内 SYSCALL_DEFINE3 read), write() (kernel 内 SYSCALL_DEFINE3 write), lseek() (kernel 内 SYSCALL_DEFINE3 lseek), ioctl() (kernel 内 SYSCALL_DEFINE3 ioctl), close() (kernel 内 SYSCALL_DEFINE1 close)など低水準な入出力関数です。
|
1028
1029
1030
1031
1032
1033
1034
| - | | | | ! |
|
恐らく仕事でコーディングルールが決まっている。あるいは教育機関で「良い」とされるコーディングルールを習ったと思います。Linux kernel にもコーディング・スタイル /Documentation/CodingStyle があります。おおよそあり得るルールだと思います。
では Linux kernel のコーディング・スタイルに従う必要が有るのか?
正直に言えば、CodingStyle に書かれている一部のルールはバグを誘引しやすいルールを含んでいると考えています。特に if () 周りの中括弧の使い方は追跡困難なバグを作りやすいと思っています。 いい方向で、なるべく違和感が無い程度のルール変更はありだと思っています。
自分が書いた Linux kernel に組み込むデバイス・ドライバは main line にマージされるのか?なさそうであれば、気にせず独自のコーディング・スタイルでと思っています。デバイス・ドライバはファイル・システム、ブロック IO エレベータ、メモリ管理、スケジューラー、ネットワーク・フィルター、セキュリティ等とは違いテストが可能な環境は限られています。kernel 内 API の大胆な変更に伴いデバイス・ドライバも修正を受けますが、それが他者によってテストされることは期待できないでしょう。
CodingStyle ファイルに書かれていないルールはどうなっているのでしょうか? いくつかはアプリケーションプログラマにとって衝撃的な現状でしょう。いくつかの例を見ていきます。
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
| - | ! |
|
345
346
347
348
| - | ! |
|
ノート
current マクロ のもう一つの特徴はプロセッサごとに実装が違うことです。ソースコードを追跡している途中で、このように多くの実装箇所を見つけた場合は、ターゲットに使っているプロセッサ、プラットホーム、.config ファイルに書かれた条件と一致するコードを追いかけます。
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
| - | | | | | | | - | | | | - - | | | ! | ! | | | | | | | | | | | | | | | | | ! |
|
仮想環境を使って Linux kernel に組み込むドライバの基本的な事柄を探っていきます。ある程度なれたら、実働ターゲットを使い実践的なドライバを作っていく予定です。
仮想環境を使い、Linux kernel の基礎を理解していく予定です。driver を書き始める前に理解し、慣れることが多くあります。あまりにも多いので飽きてしまったり、実務のために必要な時間が少なくなってしまうかもしれません。
仮想環境が十分にハードウエアをエミュレートしていれば使わなくて済む様に進める予定です。もし、Linux をインストールした PC を用意できるならば、仮想環境より実感がある経験が得られるでしょう。ms (ミリ秒) オーダーのタイミングで変化する状態などはデバイスやドライバが意図通り変化しているのか、判断・分析するための手がかりになることがあります。
raspberry pi または beagle bone black と実際のデバイスを使用して driver を開発していく予定です。
Linux kernel 対する理解を深めるにはソースを読むことです。このページで説明を加えようとしているのは自己矛盾かもしれません。ソースコードは膨大な量です。何処を読めばよいのか、grep でシンボルを探しても見つからない。といった理解を困難にする問題もできる限り解決できるようにしていきます。
デバイス・ドライバを作る上で、読む・修正する箇所はおおよそ次の場所です。関数の機能を探るためもっと他の場所も読むこともあるでしょう。
場所 | 主な内容 | 説明 |
/Documentation | 技術資料 | 00-INDEX を索引ページとして Linux kernel に関するドキュメントが格納されています。一通り読むのは大変です。grep で関数をはじめとするシンボルを検索して見つかったファイルを読んでみると、理解が進み用法が解ってくることがあります。 |
/include | ヘッダ・ファイル | 主に kernel source code で使用するヘッダファイルが格納されています。 |
/drivers | デバイス・ドライバ | 殆どのデバイス・ドライバがここに格納されています。おそらく、新しいデバイスために 1 からドライバを書くのではなく、似たようなデバイス・ドライバを手本に、デバイス固有部分を書き直す場合が多いでしょう。 |
/sound | サウンド・デバイス・ドライバ | サウンド・デバイス・ドライバを書く、あるいは修正する場合に読む必要があります。サウンド・デバイス・ドライバとその抽象化レイヤ (ALSA API) はここに格納されています。DocBook/writing-an-alsa-driver.tmpl を参照してください。おそらく多くのプラットホームでサウンド・デバイス・ドライバは完成された状態で SoC メーカーから提供されているはずです。変更するとしたら DAC, ADC, Volime, Mixer を好みの音がするチップに変える場合でしょう。 |
/arch 以下 processor/mach-your_platform | plat-your_platform | 初期化コード・コード、プラットホーム固有コード、ヘッダファイル | プラットホーム(PC で言えばマザーボードに乗っている CPU と チップセットの組)に固有のコードです。/arch ディレクトリ以下は各プロセッサ、各プラットホーム(System On Chip device, 評価用ボード) によって構成はまちまちです。PC では /arch/x86 の下にまとめられていて、デバイスを初期化するコードの一部は kernel の下に入っています。ARM の様に多様なプラットホームが存在するとサブ・ディレクトリ mach-your_platform か plat-your_platform が作られ、この中に各プラットホームに固有なコードやヘッダファイルが格納されます。 デバイス・ドライバ開発でこの付近のコードを修正する機会は、kernel 起動時にハードウエアの初期化が必要な場合、共用のグローバル変数領域が必要で初期化をする場合、SoC 内に組み込まれた IP ブロックをデバイスとして組み込む platform_device_register()、I2C device を登録 i2c_register_board_info() するなどの処理です。 Linux kernel 起動中に LED 等の表示デバイスを点灯・点滅させるなど、凝った処理を要求される場合も、修正を加える所になります。 |
*_defconfig の一覧 /arch/processor/configs/*-defconfig | make, コンパイラが参照するマクロ値 | 各プラットホームに適した make, compile 時に参照されるマクロの値定義を格納したファイルです。make platform-defconfig コマンドでこのファイルから、ソース・コード・ツリーのルートに .config ファイルを作成します。 |