【解説】12ステップステップで作る組込みOS自作入門 8thステップ ~全体の流れ~
8thステップ「スレッドを実装する」は
『12ステップで作る組込みOS自作入門』で一番難しいステップです。
つまり1記事で解説を完結するのが難しいです。
そこで記事をいくつか分割してから解説しようと思います。
本記事ではシーケンス図を使って全体の流れを説明します。
全体像で見たときに8thステップはそんなに難しくありません。
正直難しいのはこの全体像を成り立たせるためにどのようにプログラミングしているか、ということなのです。
(いやいや、全体像でも十分に難しいよという方がいたらすみません。記事が読みにくいのであれば私のせいです…。)
目次
用語
いくつか専門用語が出ているので書籍の言葉を借りつつ、なるべく自分の言葉で説明します。
- スレッド
もともとはUNIX系の言葉のようです。
組込みシステムの世界のプログラミングでは1つのmain関数に相当するため、タスクと同義です。 - レディーキュー
実行可能なスレッド一覧です。(実行中のスレッドを含みます。←重要) - システムコール
OSに対してOSの機能を使わせてほしいと通知する機能です。
書籍の中では「スレッドからしか呼べない特別な関数」です (理解する上で重要) 。
システムコールを発行する (呼ぶ) とスレッドの処理を中断してOSが処理を始めます。 - (スレッドの) スケジューリング
次に処理を実行するスレッドを決めることです。(OSが実施します) - (スレッドの) ディスパッチ
スレッドの処理を再開することです。(OSが実施します)
シーケンス図
全体を俯瞰できるように1枚の画像として貼りました。
解説部にシーケンス図を分割して貼ってあるので、詳細はそちらで確認してください。
解説
3つのパートに分けて説明します。
シーケンス図の補足説明的な使用をお勧めします。
- main関数開始~main関数終了
- startスレッド開始~startスレッド削除
- commandスレッド作成~commandスレッド削除~システムダウン
main関数開始~main関数終了
7thステップと同様にまずはmain関数が呼ばれます。
理解のために少々過激なことを言うと、main関数はOSの一部です。
初期化や割込みハンドラの登録が終わったら、初期スレッド (startスレッド) を作成します。
main関数はスレッドではなくOSの一部なので、システムコールを発行することができません。
(KOZOSはシステムコールを発行できるのはスレッドのみという作りになっています。)
OSの機能を直接使って初期スレッドを作成します。
最後にOSはスレッドのディスパッチをしてstartスレッドの処理が開始します。
startスレッド開始~startスレッド削除
8thステップでのstartスレッドの責務はシステムコールを利用して各スレッドを起動することです。
(初期化処理の一環ですね。1つのスレッドしか起動していませんが、複数のスレッドを起動することもできます。)
commandスレッド作成のシステムコール
startスレッドはスレッド作成のシステムコールを発行してcommandスレッドを作成します。
※システムコールを発行すると割込みが発生します。そのため直後に割込みハンドラ (ISR: Interrupt Service Routine) が処理を開始しています。
OSは割込みハンドラで動作します。
割込みハンドラではOSの機能を使ってcommandスレッドを作成します。
その結果、レディーキューは「startスレッド、commandスレッド」という順番になっています。
次にOSはスレッドのスケジューリングを行います。
平たく言えば次に実行するスレッドを決めます。
8thステップはラウンドロビン方式なので、レディーキューの一番先頭にあるスレッド (startスレッド) が次に実行する予定のスレッドとなります。
最後にOSはスレッドのディスパッチを行い、startスレッドの処理が再開されます。
startスレッド削除のシステムコール
startスレッドの責務が終わったのでstartスレッドを削除します。
スレッドはOSによって管理されているため、スレッド削除のシステムコールを発行することでOSにスレッド削除依頼を出します。
スレッド作成のシステムコールと同様に、システムコールを発行すると割込みが発生します。(大事なので2回目)
割込みハンドラではOSの機能を使ってstartスレッドを削除します。
その結果、レディーキューは「commandスレッド」のみになります。
次にOSはスレッドのスケジューリングを行います。
commandスレッドが次に実行する予定のスレッドとなります。
最後にOSはスレッドのディスパッチを行い、commandスレッドの処理が再開 (開始) されます。
commandスレッド開始~commandスレッド削除~システムダウン
8thステップのcommandスレッドの責務はシリアル通信とコマンド解析です。
シリアル通信経由で受信した文字列を解析してコマンドに応じた処理を行います。
特にexitコマンドを受信したときはcommandスレッドの仕事が終わります。
つまりcommandスレッドを削除します。
スレッドの管理はOSの責務なので、commandスレッド削除のシステムコールを発行します。
割込みが発生し、割込みハンドラでOSの処理が開始されます。
commandスレッドを削除した結果、レディーキューには何もなくなります。
OSはスレッドのスケジューリングを行おうとしますが、実行可能なスレッドがないのでシステムダウン (システム停止) します。
なお、システムダウンではCPUが暴走しないように「何もしない」を無限に繰り返すループに入ります。
システムコール周りの動きをまとめる
システムコールが何回か登場しました。
大事なので動きをまとめておきます。
- スレッドでシステムコールを発行する (呼ぶ) 。
- 割込みが発生してスレッドの処理が中断する。
- 割込みがトリガになって割込みハンドラが開始する。
- OSがシステムコールに応じた処理を行う。(OSは割込みハンドラ上で動作する)
- OSが次に実行するスレッドを選択する (スレッドのスケジューリング) 。
- OSが選択されたスレッドの処理を再開させる (スレッドのディスパッチ) 。
あとがき
今回も長くなってしまいました。
ちょっと文字が多いので (5/21執筆時点では3000字程度) 、流し読みするのには向かないかもしれません。
そういう意味ではPowerPointスタイルのSlideShareの方がいいんでしょうか…?
少しずつ執筆していこうと思いますが、やはり完成まで時間がかかりそうです。
以下は、書籍に対する個人の感想です。
書籍ではスレッドという言葉が使われています。
著者の方には申し訳ないですが、組込みOSの世界だと「タスク」と呼ぶ方が一般的かなと思います。
ただし、今後も書籍に合わせてスレッドという言葉を使います。
書籍ではスレッドの状態は2つ (実行可能状態、待ち状態 (スリープ) ) と説明されています。
しかし個人的にはスレッドの状態を3つ (実行状態、実行可能状態、待ち状態) にした方が分かりやすいと思います。
(ITRONの仕様でもタスク状態に「実行状態」があります。)
スレッドの状態を3つにすることでレディーキューを「実行状態および実行可能状態のスレッド」と定義することができます。