OSがあまりにわからなかったからとり あえず自分でOSつくっちゃった話 03-150508 野上和加奈
動機 もう6学期だし真面目に勉強しようと思った 真面目に授業聞いてみたけどよくわからなかった Amazonみてたら OS自作 という文字列を発見 話し聞いてもよくわからないしもはや自分で作っちゃえばいいんじゃない 駒場祭付近暇だしそこで 一気に作っちゃおう
0 step 本選び OS自作についての本はいくつかあった リアルタイム性など 何かに特化したものを除くと有名なものは以下の2つ 候補1 30日でできる OS自作入門 小林秀実著 候補2 12ステップで作る組み込みOS自作入門 坂井弘亮著
0 step 本選び 候補1 30日でできる OS自作入門 小林秀実著 プログラミングの基礎からはじめて 30日後にはウィンドウシス テムを有する32bitマルチタスクOSをフルスクラッチで作り上げ るという入門書 ビギナーでも無理なく作成できるようPCの仕組 み アセンブラ Cの解説から始まり 試行錯誤を繰り返しながら アルゴリズムを学びつつ たのしく自由な雰囲気でOSをゼロか ら構築していくという 他に類を見ない手法による 趣味と実用 と学習を兼ね備えたOS作成の入門書
0 step 本選び 候補2 12ステップで作る組み込みOS自作入門 坂井弘亮 著 手軽に購入できるマイコンボード上で動作する独自の組込 みOSをフルスクラッチで自作する ブートローダーも自作し ますので 電源ONの一番最初の動作からの理解 学習 が可能です また内容は12ステップに分かれているため 講義や輪講 実習などでの教材利用にも向いている
0 step 本選び 授業でやる内容に関して理解が深まるか 駒場祭による休講 祝日 実質4日 で実装できるか 難しすぎないか などを基準に選定 12ステップで作る組み込みOS自作入門 に決定
はじめに 組込みOSとは ある特定の機器に搭載して制御するためのOS ここではマイコンボード上で動くOSを実装 このOSでなにができるか I/Oはシリアル通信だけ PCからの文字入力に応答したり 文字を出力したりする
はじめに それだけの機能でOSといえるか OSの基本機能は資源管理 スレッドによるCPU時間の管理の実装 固定サイズのメモリ管理の実装 シリアル通信でI/Oの管理の実装 よってOSと言っても良いのではないか
はじめに このOSには実用性がないのではないか 使う ではなく 作る ことが目的 実用性は問題ではない
はじめに マイコンボードH8/3069Fの組み込みOSを実装 PCとシリアルケーブルでつなぐ 端末エミュレータでPCからマイコンを操作 PC側でマイコンの動作の様子を見る
開発の流れ 第1部 ブート ローダーの作成 第2部 OSの作成 1st step 開発環境の作成 7th step 割込み処理を実装する 2nd step シリアル通信 8th step スレッドを実装する 3rd step 静的変数の読み書き 9th step 優先度スケジューリング 4th step シリアル経由でファイルを転送する 10th step OSのメモリ管理 5th step ELFフォーマットの展開 11th step タスク間通信を実装する 6th step もう一度 Hello World 12th step 外部割込みを実装する
1st step 開発環境の構築 H8 3069Fネット対応マイコンLANボード 完成品 通販コード K-01271 発売日 2006/01/23 1台 3,750 税込 秋月電子通商で購入可
1st step 開発環境の構築 やること 開発のための環境構築 Hello World を出力するプログラムを動作させる
1st step 開発環境の構築 割込みベクタによって実行開始するアドレスを定義 スタートアップのstartという関数から始まるように定義 スタートアップをアセンブラで実装 startという関数はスタックポインタの設定を行ったあとmain()を呼び出す メイン関数ではputs()という関数で Hello World! という文字列を出力 ライブラリ関数として1文字送信関数putc()とそれを複数回用いた文字列送信関数puts() を実装
1st step 開発環境の構築 その他 ライブラリ関数のヘッダファイル シリアル デバイス ドライバ メモリをあつかうリンカスクリプト なども実装した
1st step 開発環境の構築 実行結果
2nd step シリアル通信 やること 必要なC言語の標準ライブラリの関数の実装 数字を表示する関数の実装
2nd step シリアル通信 memset() : メモリを特定の倍てデータで埋める memcpy() : メモリのコピー memcmp() : メモリの比較 strlen() : 文字列の長さ strcpy() : 文字列のコピー strcmp() : 文字列の比較 strncpy() : 長さ指定での文字列の比較 などのおなじみのライブラリ関数を実装
2nd step シリアル通信 putxval() : 整数値を16進数で出力するライブラリ関数を実装 当然 それに伴って ヘッダファイルの変更なども行った
2nd step シリアル通信 実行結果
3rd step 静的変数の読み書き やること 静的関数の書き換えができるようにする
3rd step 静的変数の読み書き メモリをあつかうリンカスクリプトを修正 いままでROMだけを扱っていたがRAMも扱えるようにメモリ領域を定義 各セクションをどこに配置するか定義 静的関数が配置されるdataセクション bssセクションはramとromの両方に配置さ れるように定義 スタックの定義の追加
3rd step 静的変数の読み書き 実行結果
4th step シリアル経由でファイルを転送する やること XMODEMの実装
4th step シリアル経由でファイルを転送する XMODEMとは シリアル通信でファイルを送るためのプロトコル
4th step シリアル経由でファイルを転送する XMODEMのプロトコルに従ってXMODEMを実装 ファイルの受信処理の開始などをコマンドで操作できるようにmain関数がコマンド入力を 受け付けるように変更 load で受信処理開始 dump でメモリを16進で出力 それ以外の入力には unknown とかえす
4th step シリアル経由でファイルを転送する リンカスクリプトで受信バッファのための領域を定義 デバイスドライバにデータ受信のための関数を追加 コンソールでの1文字受信関数getc(),文字列受信関数gets()をライブラリに追加 マイコンボードの動作が確認できるようにエコーバックも実装している
4th step シリアル経由でファイルを転送する 実行結果
5th step ELFフォーマットの展開 やること OSを起動させるための 実行形式ファイル を転送する それを解析して メモリ上に展開できるようにする ここではセグメント情報を表示させるとこまで
5th step ELFフォーマットの展開 実行結果①
5th step ELFフォーマットの展開 実行結果②
6th step もう一度 Hello World やること 実際に実行形式ファイルを展開して Hello World を作成する
6th step もう一度 Hello World ブートローダー側 5th step で表示できるようにした部分をmemcpy()によって実際に動作させるメモリ上に コピー main関数をロードしたプログラムに処理を渡すように変更
6th step もう一度 Hello World OS側 基本的にはここまで書いてきたコードが使える コンソールからの入力を受付け echo,exitコマンドに対して処理を行うようなmain関数を 実装 OSようにリンカスクリプトのメモリ定義を書き直す
6th step もう一度 Hello World 実行結果
7th step 割込み処理を実装する やること 割込み処理ができるようにする
7th step 割込み処理を実装する ブートローダー側 割込み処理の前と後におこなう処理 メモリの退避 復帰など をアセンブラで実装 割込みベクタを変更し割込みが起こった時に実行すべきアドレスを指定 その他 割込みベクタ関連の定義や初期化などを実装
7th step 割込み処理を実装する OS側 シリアル受信割込みを受け付けるように シリアルデバイスドライバに割込み機能を追加 main関数を割込みによって動作するように変更
7th step 割込み処理を実装する 実行結果
8th step スレッドを実装する やること スレッド動作を実装する OSの持つサービスを利用するための システム コール を実装する
8th step スレッドを実装する ブートローダー側 リンカスクリプトの書き換え スタックを用途別に明示的に分離 それに伴い 割込み処理やスタートアップで用途にあったスタックを使うように変更 ブートローダー側はこれで完成
8th step スレッドを実装する OS側 リンカスクリプトの書き換え 同様 スレッドのディスパッチ処理を追加 スレッドの生成 終了 システムコールの呼び出しを行う関数などを実装 リンクリスト構造でスレッドを管理
8th step スレッドを実装する 実行結果
9th step 優先度スケジューリング やること スレッドに優先度を導入する システム コールを追加する スレッドの スリープ 状態を実装する
9th step 優先度スケジューリング 優先度を追加 レディ キューの配列を優先順位の高い順に検索し 動作可能なスレッドを探す 優先順位の高い方から実行 スレッドの切り替えを行う関数や スレッドをスリープ状態にする/起こす関数 優先度を変 更する関数 スレッドIDを取得する関数を実装
9th step 優先度スケジューリング 実行結果
10th step OSのメモリ管理 やること メモリ管理ができるようにするために malloc free などを実装する
10th step OSのメモリ管理 あらかじめ16バイト 8個 32バイト 8個 64バイト 4個の領域を用意する それらをリンクリストでつなぐ メモリが要求されたら収まる最小サイズの領域をリンクリストから切り離して与える 開放されたらまたリンクリストに繋ぎ直す
10th step OSのメモリ管理 実験結果
11th step タスク間通信を実装する やること スレッド間で情報をやりとりするためのタスク間通信の機能を実装する
11th step タスク間通信を実装する メッセージをいくつかのボックスにわけレディーキューでつないで送信する
11th step タスク間通信を実装する 実行結果
12th step 外部割込みを実装する やること 1文字受信割込みを受け付ける 割込み処理 を実装する コマンド応答スレッドを作成する
12th step 外部割込みを実装する やること 割込みハンドラからシステムコールを呼び出せるようにトラップ命令を発行せずにシステ ムコールの処理を関数呼び出しするサービス関数を実装
12th step 外部割込みを実装する 実行結果