OSがあまりにわからなかったからとりあえず自分でOSつくっちゃった話 (1st step)
はじめに
題名にあるとおり今までぼーっとふわふわーっと生きてきたらOSの授業があまりにわからなかった(他の授業はわかるとは言ってない)ので、「これはもう自分で作っちゃったほうが早いんじゃない?」と思ったので自分で作ってみました。
というとなんか強い人が凄いことをやったみたいになっていますがやったことは簡単に言うと①OS自作の本を購入②まねして実装の2つだけ。
もともとただの自己満足としてやってみようと思っていたのですが、せっかくやるならアピールしとこうってことで、OSの先生に話して、OSの授業で発表することになりました。
で、そうなるとさすがに本買って真似して実装しただけだと先生にも「え、それって本の写経しただけだよね?サルでも出来るよね?」って言われないにしても、思われちゃうので、最初は自分で拡張してみるつもりでした。
が、一応ちゃんと本を読んで理解しながら進めたはずなのに終わってみるとわかったようなわかってないようなモヤモヤ感がすごい・・・。結局何したんだかいまいちわからないという感じになりました。
「拡張とか言う前にこのモヤモヤ感をなんとかせねば!」ということで、拡張するのは将来の自分への課題として、どのような実装を行ったのかを適宜調べながらもう一度しっかりまとめ直そうということにしました。さすがにこれだけだと先生に「え、拡張するって言ったよね?結局大したことしてなくない?」って思われちゃうので、汎用OSではどんな実装がされてるかなど、今回作ったOSと比較しながらまとめようと思います。
結果的に授業でやったようなことを自分でしっかり調べてまとめるみたいな感じになるわけで、結局OSがわからなかったからOSつくってみたけどやっぱりよくわからないからOS真面目に勉強した話みたいになりました。
0 step 本選び
まずAmazonで調べたところ、OS自作でまずヒットした本は「30日でできる! OS自作入門」という本、コメントも多く評価も高いのでいい本なんだと思います。はじめはこれにしょうかと思っていたのですが、コメントをよむとどうやら本当に30日でやるには相当頑張らないといけないっぽい、また開発環境はwindowsに限られるっぽいということがわかりました。さすがにそんなに時間かけれないなぁという思いからこの本は保留にして他の本を探しました。
そこで見つけたのが、今回利用した本「12ステップで作る組込みOS自作入門」でした。コメント数はさっきの本よりは少ないのですがこちらも評価の高い本です。組み込みOSってなんなのかよくわかっていなかったのと、なんかマイコンボードを使うとかで、どうなんだろうと思っていたのですが、コメントの中でゼミで学部3年生にやらせていて朝9時から夕方8時くらいまで毎日作業ということで、実働10時間を6日間で完成させているというのがあって、割と短期間でできそうだと思ったのと、「なにより大学生や社会人1年目の方が読まれると信じられないくらいレベルアップできると思います。」というコメントを発見し”圧倒的成長学科”の自分としてはやらないわけにはいかないという気持ちになりました。
その後本屋にも行ってほかにもいくつかの本の中身を実際にみてみましたが、OSの授業の内容とも近いものとなると「12ステップで作る組込みOS自作入門」が最適と判断しこの本に従って開発を行うことにしました。
この本にはサポートページもあるのでそこも参照していただければどのようなものなのか解ると思います。
1st step 開発環境の構築
*やったこと*
gccやら、minicomやら、フラッシュROM書き込みツールやらのインストール
ただインストールするだけなんですが、いろいろと本との環境の違いやらで手こずりました。12ステップの中で一番手こずったとおもいます(´・ω・`)
そして、今回作成したファイルは次のとおりです。
main.c : 「Hello world!」を出力させる普通のCのプログラム
startup.s : アセンブラで書かれていてスタックポインタの設定とmain()の呼び出しなどを行っている。プログラムはこの中の_start:から実行される
vector.c : 割込みベクタの定義。start()から実行されるようにポインタを設定
lib.h lib.c : ライブラリ関数。今回はputsとputcだけ実装。
serial.h serial.c : シリアル通信を行うためのいろいろが書かれている。
defines.h : charやlongなど様々な型の定義。
ld.scr : リンカスクリプト。コンパイルして作成される実行形式ファイルのメモリ配置を定義している。
Makefile : ビルド方法のマニュアル的なもの。
*説明*
組み込みプログラムにおいて、プログラムの実行はstart.sの_startから始まる。_startではスタックポインタの設定を行い、main()にジャンプする。では、どのようにして、_startから処理を開始するのだろうか。多くのCPUには割り込みという機能がある。これはCPUがある処理をしている最中に「割込み」という指示が入ると、別の処理にはいるというもの。割込み時に行う処理を一般的に割込みハンドラと呼ぶ。割込みハンドラの処理が終了すると、CPUは中断していた処理を再開する。割込みを実現するには、割込み発生時にどのハンドラを実行するのかを設定する必要がある。これは大きく分けて2つの方法がある。
①割込み発生時には、特定のアドレスから実行する。
②割込み発生時には、どのアドレスから実行するか特定のアドレスに設定しておく
①の方法は、割込みハンドラを配置するアドレスをCPU側で決め打ちにしてしまい、割込みが発生したらそのアドレスに強制的に処理がとぶというもの。
②の方法は、割込みハンドラを配置するアドレスを特定のアドレスに記述しておくというもの。
この「ハンドラのアドレスを記述しておく特定のアドレス」を割込みベクタと呼び、この方法をベクタ割込み方式と呼ぶ。割込みが発生すると、CPUは割り込みベクタを参照してハンドラの配置場所を知り、そこに強制的に処理がとぶ。H8はベクタ割込み方式で、割込みベクタの種類に応じて、アドレスが0x000000~0x0000ffのメモリ上に配置されている。割込みの一つにリセットベクタがある。H8は、リセットが発生するとリセット・ベクタを参照してそこから動作を開始する。H8では、割込みベクタの先頭がリセット・ベクタになっている。vector.cでvectorsという配列を定義した。これが割込みベクタの設定である。
vector[0]にはstart()関数のアドレスが設定されている。これはstartup.sで定義されている_startのことである。
H8の割込みベクタは0x000000~0x0000ffというアドレス上にあるため、vectorsの内容は0x000000~0x0000ffのアドレスに配置する必要がある。これはld.scrで行っている。
作成したコードはここにあげてあります。基本的には本に書かれているもの、サポートページにあるものと同じですが、自分の中でわかりやすいようにコメントを追加してあります。
これでHello World!できました。