どうも。半年ぶりの投稿のPiBVTです。
今回は、GW中に「12ステップで作る組込みOS自作入門」をしたのでそのことを書きたいと思います。
12ステップで作る組込みOS自作入門とは?
H8/3069Fというマイコンボードを利用して、少しずつステップアップしながら最終的にはOSを動かそう。という主旨の本です。
気になる方は色々検索してみてください。
なんでGW中に?
で、何でGW中にこんなことをやっていたのかというと、すべてはこのツイートから始まりました。
セキュリティキャンプ行きたい
— PiBVT (@PiBVT) April 21, 2018
今年のセキュリティキャンプのテーマが発表されて専門講義に「OSフルスクラッチ開発」という項目を見つけました。
しかし、参加するには、課題に対する回答を書かないといけません。OSについていろいろ調べたり妄想していると…
OS作りたい欲求が爆発したのでGWに12ステップ組み込みOS自作やる。
— PiBVT (@PiBVT) April 27, 2018
となりました。普通のコンピュータ大好きな学生にとってはよくあることですね。
ちょうど大学図書館にも本があったので早速借りて読み始めました。
準備物
作りながら読み進めるために、27日の昼にはH8/3069Fマイコンボードを購入しました。
3連休中は届かないだろうと思っていると…
やるぞやるぞ pic.twitter.com/62y8Zl98Of
— PiBVT (@PiBVT) April 28, 2018
次の日の昼には届きました。秋月電子さんと佐川急便さん優秀すぎ。
シリアルケーブルは持っていたので新たには注文しませんでしたが、これがトラブルとなります…
前半三連休の様子
とりあえず、マイコンボードも届いたのでgccクロスコンパイラ等の環境構築を済ませ、
1stステップのHello Worldをすべくせっせと写経し始めました。
本の内容は、ステップごとにソースコードを写経しそのコードに関する解説がついてくる構成なのでかなり分かりやすかったです。
1stステップは「Hello World」とシリアル通信で表示するための制御プログラム、いわばドライバみたいなものをCで書き、処理関数への突入用にアセンブリを数行書いた程度でした。写経自体は1時間半ほどで終わりビルドもできたので早速バイナリをマイコンボードに書き込もうとしました。が、
書き込めん!なんでや!
Arch Linuxが悪いのかWindows環境でも試してみましたが、全く書き込めません。結局、深夜過ぎまで試行錯誤していましたが全くうまく行きません。ネットの海をサーフィンした結果、300円で購入したシリアルケーブルが悪いようなので半ギレになりながら、他の方の実績があるiBUFFALO USBシリアルケーブルと、RS-232C ストレートケーブルを購入しました。届くのには数日かかるのでそれまではせっせと本を読み進めていました。
「はやくへろーわーるどしたい」と5分おきに言いながら2ndステップを読み進めます。このステップでは1stステップのシリアル通信プログラムの解説を読んだり、putsなどのシリアル出力系の関数を書いたりしていました。
2ndステップを読み終わっても注文したものが届かないので3rdステップを読み進めます。個人的には前半三連休の中で一番ワクワクした内容でした。静的変数等のメモリ領域割当についてのステップだったのですが、リンカスクリプトを駆使してROM,RAMの領域を操作したり、初期値を持つ変数と持たない変数の領域確保の違いを初めて知ったりしました。
ここでようやく荷物が到着し、試しに書き込んでみると何事もなかったかのようにあっさりと「Hello World」しました。
やっと動いた pic.twitter.com/dFxFFDoARK
— PiBVT (@PiBVT) April 30, 2018
2ndステップ、3rdステップのプログラムも無事動き、次は4thステップへと進みました。
4thステップでは、シリアル通信でデータを転送し、RAMの領域上に展開するプログラムを書きましたが、3連休中には動きませんでした。
ちなみに3連休での12ステップOSの進捗は4thステップのブートローダー起動後のデータ転送まででした。デバッグは出来てないので動きません。 つらい。
— PiBVT (@PiBVT) April 30, 2018
中二日間の平日
講義にはちゃんと参加し、帰宅後せっせと作業しました。4thステップのプログラムは一行だけ抜けている部分があり、受信後無限ループになっていたのを修正すると無事動きました。
デバッグ完了 pic.twitter.com/0angCFkXAT
— PiBVT (@PiBVT) May 1, 2018
dumpコマンドで送り込んだデータのバイナリを表示し、一致するかチェックすると無事一致しました。少し感動。
まだまだ元気なので5thステップに進みます。5thステップではELF形式のバイナリを読み込み、ヘッダーなどを取得したりRAM上に展開したりするプログラムを書きました。普段何気なく実行しているELF形式ファイルの中身がどうなっているのか知れたのはいい経験です。ヘッダーを読んでいると実行アーキテクチャやメモリセグメントなど予想以上に情報量がありました。で、ビルドし書き込み実行すると、
5thステップ終了 pic.twitter.com/1QbAk1srwj
— PiBVT (@PiBVT) May 1, 2018
しっかりとヘッダーを取得してくれました。
5thステップがうまく動き調子に乗ったのか6thステップに突入しました。次の日は6時起きなのにそんなことは完全に無視です。6thステップはシリアル通信経由で実行バイナリを受け取り、実行するブートローダーを完成させます。1stステップで動かしたHello Worldを実行バイナリ化し、ブートローダーで読み込んで実行します。
ステップ6th終了。
ブートローダーは完成した pic.twitter.com/EKHg42vKyT— PiBVT (@PiBVT) May 1, 2018
1時間ちょっとで動き、中身も大して難しくなかったので楽に読み進めることができました。
5/2水曜日は、前日夜遅くまで作業していたせいで教科書を忘れるやらバイトで寝そうになるやら散々で迷言を吐く以外何もできませんでした。
私の中では寝るまでが今日なので明日も明後日も頑張れば今日なのです
— PiBVT (@PiBVT) May 2, 2018
意味不明です。
後半4連休(作業自体は2日間)
さあ、待ちに待った後半4連休です。木曜日と金曜日はサークルで夜も「攻殻機動隊S.A.C」の鑑賞会をしながら作業していました。
7thステップは割込み処理の実装です。OSにとって重要な要素の1つでワクワクしながら読み進めました。ここでは、シリアル通信を割込み処理で行えるようにしました。
割込み処理突入時のレジスタやスタックポイントの退避、割込み処理終了時の復帰はアセンブリで数十行でしたが、使っている命令は殆ど同じなので簡単でした。で、
7thステップ 割込み処理のハンドル完了 pic.twitter.com/s6FPBuKMce
— PiBVT (@PiBVT) May 3, 2018
動きました。
次は8thステップでスレッドの実装です。ここらへんまで来るとOSって感じがしてきてテンション上がりまくりですね。「ひゃー楽しいい」とか言いながらやってました。(本当です) スレッド処理に必要なタスク情報を格納するTCB(タスクコントロールブロック)を構造体で実装し、実行可能なスレッドを保持するレディ・キューの実装は、うまい具合に考えられていて勉強になりました。スレッドの実装だけあって、それなりのコード量はありましたが無事ビルドし、
ステップ8th スレッドの実装完了 pic.twitter.com/n7JmUbAhur
— PiBVT (@PiBVT) May 3, 2018
スレッドが1つ動きました。この段階ではスレッドはラウンドロビンなスケジューリングになっているので9thステップは優先度を実装します。
9thステップはスレッドの優先度実装です。スレッドをどのような順番で実行するのか、割と実行効率に直結する部分です。スレッド関連のシステムコールを実装し、優先度に関する項目を追加したりしていましたが、8thステップのレディ・キューの概念がここでかなり役立ちました。(レディー・キューを優先度が高い順にすることで、必然的に優先度が高いスレッドから実行される。) 若干のバグを埋め込んで正常に動かなかったりしましたが、なんとかデバッグして、
9thステップ 優先度スケジューリング完了 pic.twitter.com/OnjWzpu2w0
— PiBVT (@PiBVT) May 3, 2018
動きました。この時点で深夜1時40分です。だいぶ疲れてきましたがまだまだです。
10thステップはメモリ管理です。有名なmalloc()やfree()などの動的なメモリ領域確保のシステムコールを実装します。今回のマイコンにはMMU(メモリ管理ユニット)がないため、仮想メモリやメモリ保護機能はありません。そのため、ちゃんとメモリ管理をしないとOSが破綻してしまいます。ここでもスレッドの管理と同様にリンクを利用したデータ構造が用いられていて、個人的にはかなり面白い部分でした。眠かったけど。眠い目をこすりながら頑張った結果、
10thステップ OSのメモリ管理完了。 寝る。 pic.twitter.com/LaRtDpyFOo
— PiBVT (@PiBVT) May 3, 2018
動的に確保した領域に文字列を保存できました。ここでついに力尽き、仮眠を取ることにしました。ちなみに大学内です。
硬い長椅子の上で長時間寝れるわけもなく、目を覚ますと…
朝5時半から始まる宇宙物理学 pic.twitter.com/WF8eRffjYO
— PiBVT (@PiBVT) May 3, 2018
徹夜で映画「インターステラー」を見ながらPythonで物理シミュレーションをしていたサークルの仲間がブラックホールについて計算していました。どうやらブラックホールの半径について計算しているようでしたが、何をしているのかよく分かりませんでした。自分がブラックホールになった場合の半径を計算して満足そうにしていました。
そんなことはさておき、11thステップのタスク間通信を実装します。スレッドでプログラミングをしていると割込み処理などで意図しない変数が書き換わる可能性があります。その対策としてタスク間通信によってスレッド同士で同期をとり、データを書き換える恐れのあるサービスをスレッド化することで、問題を回避しています。この実装もメッセージボックスというものを利用していて面白かったです。まだ覚め切らぬ頭で書き、
11thステップ タスク間の通信実装完了 pic.twitter.com/0AVmd04q5W
— PiBVT (@PiBVT) May 4, 2018
動きました。
いよいよ最終ステップの12thステップ外部割込みの実装です。ここではシリアル通信とコマンド応答部分を割り込み処理とタスク間通信を利用して実装します。なのですが、このステップは殆ど記憶に残ってないです。システムコールや割込みハンドラが複数出てきて寝不足の頭には辛いところがありました。とにかく写経ロボとなりながら書き、
12thステップ 外部割り込み実装完了
おわったーーーーー!! pic.twitter.com/0LXVUn2Bia— PiBVT (@PiBVT) May 4, 2018
動きました。ここで本の内容は終了ですが、タイマやDRAM、LANなど遊べる要素はまだまだあるので少しずつやっていきたいですね。
まとめ
結局ちょうど1週間で終わりましたが、すごい良い経験だったと思います。x86での開発はかなり手間がかかるらしいのですが、マイコンは機能が単純なだけあって書いたプログラムも理解しやすかったです。実際に書きながら、「OSってこうなってんのね」とか「簡単なやつなら自分でも作れるかも」と思っていました。実際作れるかどうかは分かりませんが。あと、C言語とアセンブラのみでの開発だったのですが、C言語の構造体の使い方や管理のしやすいプログラムの書き方を経験できたのはよかったです。これからも少しずつ勉強していくつもりなので、興味のある方は一緒にどうでしょか笑
みなさんも是非OS開発を体験してみてください。きっとコンピューターに対する目線が変わることと思います。
コメント
非常に楽しそう。
大変面白そう。