「プログラミング」カテゴリーアーカイブ

OS自作入門17日目&18日目

コンソールの話でした。
コンソールと一口に言っても、やっていることは結構複雑なんだなと思った。今は、キーボードからしか入力を受け付けていないわけで、そのせいもあるのだろうけど。
harib14eで記号も入力できるようになっていますが、用意されたQEMUでは入力できません。最新のQEMUで試してみるとちゃんと入力できました。-std-vgaというオプションをつけないと、画面が1280x1024になってしまいますけど。アンダースコアとバックスラッシュが入力できないのはなぜかな。
カーソル点滅の制御などは、プロセス間で通信をやらないといけないわけですが、全部FIFOを使ってやっているのですね。これが、OSの特徴といえるのかな。

OS自作入門15日目&16日目

マルチタスクの話だった。
プリエンプティブとノンプリエンプティブの差って、案外小さいんだなと思った。実際、タスクの優先度をつけたりするのは、プリエンプティブでなければできないけれど。
処理する時間で優先度をつけるのは一般的なんですかね。
それに、カーネル内部の関数も1つのタスクとして独立して動かせるってのが新しかった。普通、アプリケーションやサーバを独立したタスクとして動かすことは、想像できるけれど。Linuxで、カーネルのログをとったりするデーモンも、こういう実装になっているんだろうか。起動の最初のほうで、いろいろ立ち上がっていることは気づいていたけれど。調べたことはないけど。
別にカーネルであろうとアプリケーションであろうと、つまり、特権レベルが違っていなくても、ある関数をタイマ割り込みで切り替えているんだから、同じなのか。
逆に、スレッドなどもTSSを1つ使ってGDTに登録してしまうという実装もありなんですね。コストがかかるけれど。
ただ、LinuxもWindowsも、プロセスの管理はx86の機能を利用せずに自前でやっていると聞いたことがある。x86_64になると、互換性のために残っているという感じだと思います。この辺、どうなっているのでしょう。

OS自作入門13日目&14日目

この前使ったタイマのバイナリを使うと、1/1000秒のタイマが作れます。
timer.cのinit_pit()の中で、こんな風に、0x04a9をPITに設定すればいいです。
io_out8(PIT_CNT0, 0xa9);
io_out8(PIT_CNT0, 0x04);
RTCを使ったタイマも作ってみましたが、やっていることはPITと同じなのでした。割り込みの終了をPICに通知するときは、マウスがやっているようにPIC0とPIC1の両方に通知しないといけなかったです。
14日目になって、ダイアログボックスに文字が入力できるようになって少し楽しいと思った。

OS自作入門12日目

問題のタイマのお話。ちょっと、QEMUの内部まで見てみようと思う。
PCには、PIT(Programmable Interval Timer)とRTC(Real Time Clock)というタイマに関するものが2つあって、それぞれirq 0とirq 8につながれている。
RTCには、日時の情報とかがあるみたいです。
Hariboteのクロックを実際の時間と比べると、少し遅い。1分で4秒くらい遅くなります。なぜ?
PITを1msで割り込みを発生しようと、count=1193=0x04a9を入れてみると、タイマは少し速くなりますけど、1.6倍くらいにしかなりません。QEMUのタイマには限界があるのかな。
WinXPホストのクロックを起動したりすると、hariboteのクロックが一瞬停止したりするのが見れる。Kqemu/Qvm86を使ったときにフリーズしてしまうのはこんなのが関係しているのだろうか。
WinNTは、RTCを見て時計の較正をしているそうです。Win2k/XPも同じなんだろうか。どんな較正なのかわからないですけどね。
Windows上のQEMUではtimeSetEventというマルチメディアタイマを使っていますけど、1msを刻むことはどうやってやっているのでしょう。
なんか、疑問ばかり。

OS自作入門7日目&8日目

FIFOは、読み書きの位置のほかに長さの情報を持っているというのがポイントになるかな。
もし、位置を変えることと、長さを変えることの間で割り込みが入ると、長さが0でも読み書きの位置が違うという変なことになるけど、そのへん割り込みの禁止をうまく使っているなと思った。
OSの起動のところは、3日目に調べたのでそれでいいかな。

OS自作入門5日目&6日目

この開発環境って、関数名を間違えるとWarningがでるだけで、エラーにならない。バイナリができてしまうので、走らせて見ると、突然OSが暴走し始めてなかなかバグが見つからないということがあった。
Makefileの一般規則で書いた場合、生成された中間ファイル.nasとか.gasが削除されるみたいです。こういうものなんでしょうか。
割り込みが動き出すと、システムが動いてるっていう気がする。割り込みの動作をイメージするのは難しいと思う。以前、デバッガを動かしていたときにステップ実行で次の行に行こうとすると、突然見知らぬコードが実行されてなかなか次の行に行けなかったことがあります。後で気づいたら、タイマの割り込みハンドラが実行されていたのでした。それで、割り込みってこういうものなんだと納得したことがあります。

OS自作入門4日目

読んだ。
用意されている開発環境は、CygwinやMinGW/MSYSのmakeではうまく動かないようです。
SET PATH=
としてPATHを消すと用意されたものが動くようです。
MinGW/MSYSのrxvtで動かすときは、Makefileを
COPY = cp
DEL = rm -f
としてやるといいです。haribote.sysを作るときは、copy /Bの代わりにcatが使えます。
cat asmhead.bin bootpack.hrb >haribote.sys
コマンドの前に-をつけると、エラーがあっても続けるという意味になるんですね。
-$(DEL)とか。
ポインタが説明されていますけど、自分の場合はgdbで変数の実際の値を何度も確認することで理解したように思います。
ただ説明を聞いただけでは、なかなか実感することは難しいと思うので、値を書き出したりgdbで確認したりするといいのではないかと思いますけどね。

OS自作入門3日目

読んだ。
この辺でつまずく人が多い気がする。bimとかhrbとか出てきて気になった。せっかく起動から追っかけてきたのにというのもあって、少し調べてみることにしました。
bimとhrbは、情報を見つけました。
omake/tolsrc/bim2hrb.cの引用。
******************************
[ .bimファイルの構造 ]
+ 0 : .textサイズ
+ 4 : ファイル中の.textスタートアドレス(0x24)
+ 8 : メモリロード時の.textスタートアドレス(0x24)
+12 : .dataサイズ
+16 : ファイル中の.dataスタートアドレス
+20 : メモリロード時の.dataスタートアドレス
+24 : エントリポイント
+28 : bss領域のバイト数
+36 : コード
[ .hrbファイルの構造 ]
+ 0 : stack+.data+heap の大きさ(4KBの倍数)
+ 4 : シグネチャ "Hari"
+ 8 : mmarea の大きさ(4KBの倍数)
+12 : スタック初期値&.data転送先
+16 : .dataのサイズ
+20 : .dataの初期値列がファイルのどこにあるか
+24 : 0xe9000000
+28 : エントリアドレス-0x20
+32 : heap領域(malloc領域)開始アドレス
*******************************
.bimファイルも、.hrbファイルも、上のような構造のヘッダーを持ったファイルになっています。WCOFFという形式は、調べてみたけどわからなかった。普通のCOFFと同じなのか。
Haribote.sysは、asmhead.binとbootpack.hrbを単純にくっつけたものになっているので、
asmhead.bin本体
bootpack.hrbのヘッダー
bootpack.hrbの本体
の三層構造になっています。これが、起動のときに重要な役割をします。
パソコンの電源を投入後、フロッピーが読み込まれて、IPLが実行され、asmhead.binに制御が移るところまではこれまで説明されているのでわかると思います。
その後、asmhead.nasでは、bootpack以下をBOTPAK(0x280000)に転送しています。bootpack:というラベルはasmhead.nasの最後にありますが、上に書いたようにasmhead.binの後ろにはbootpack.hrbがあるので、bootpack.hrbを0x280000に転送していることになります。また、最後にbootpackの起動のところで
JMP DWORD 2*8:0x0000001b
として2番目のデスクリプタの0x1bにジャンプすることで処理が終わっています。2番目のデスクリプタというのは、コピーされたbootpack.hrbがあるところなので、bootpack.hrbの0x1bに飛ぶことになるけど、0x1bというのが謎だった。
bootpack.hrbのヘッダーを見るとちょうど+24の0xe9000000の最後のバイトを指している。バイナリエディタで調べてみると、こんな風になっている。
+24: 0x00 0x00 0x00 0xe9 (0xe9000000)
+32: 0x09 0x00 0x00 0x00 (エントリーアドレス)
ここで、bootpack.hrbの0x1b番目(27番目)から並べてみると、エントリーアドレスはリトルエンディアンなので並べかえて、
0xe9 0x00000009
となって、これを逆アセンブルすると、
JMP _HariStartup
になります。つまり、0xe9というデータを実行可能なコードとみたてて、_HariStartupにジャンプするようになっていました。この_HariStartupというのは、obj2bimで、bootpack.bimにリンクされるライブラリの中にあります。ソースコードはomake/tolsrc/hrblib0a/startup.cにあって、内部でCのソースコードのHariMainを呼んでいます。そこでやっと、bootpack.cのHariMainが実行されます。
HariMainの起動までを要約すると、こんな感じです。
BIOSが、フロッピーのシリンダー0、ヘッド0、セクタ1の512バイト(IPL)を、0x7c00に読み込みます。
[ipl10.nas]
フロッピーのシリンダー10まで、0x8200を先頭に読み込み。Haribote.sysが読み込まれます。
Haribote.sysの先頭asmhead.bin(0xc200)にジャンプ。
[asmhead.nas]
Haribote.sys内のbootpack.hrbを0x280000を先頭にコピー。
コピーしたbootpack.hrbのヘッダー(0x280000 + 0x1b)にジャンプ。
[bootpack.hrbのヘッダー]
bootpack.hrb本体のHariStartupにジャンプ。
[startup.c]
HariMainを呼び出す。
[bootpack.c]
HariMain()を実行します。
フロッピーの内容を1度、メモリーにロードした後、コピーして実行しているところが複雑になっているのかな。