30日でできる!OS自作入門パート10(15日目①)
2018-11-04
気がつけば1カ月ほど空いてしまいましたがまだ終わっていません。ということで続けていきます。
今回のサブタイトルは『マルチタスク-1』です。
マルチタスクをCPUにさせるには?
実はCPUが1つしかなくてもマルチタスクはできているようです。その場合同時に複数のプログラムを動かしているのではなく、忍者の分身の術のようにAのプログラムを少し動かして次にBのプログラムをちょっと動かして…と処理しているようです。そうなるとこの切り替えを高速にしないと同時に動かしているように見えないですね。このプログラムの切り替えを「タスクスイッチ」と言うようです。
CPUにタスクスイッチをしなさいと命令するとCPUはレジスタの値をすべてメモリに書き込みます(後で実行を再開するため)。その後にこれから実行するプログラムのためにメモリから全部のレジスタの値を読み込みます。これで切り替えは完了となります。
レジスタの内容はどのようにメモリに書き込まれるのか?
struct TSS32 { int backlink, esp0, ss0, esp1, ss1, esp2, ss2, cr3; int eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi; int es, cs, ss, ds, fs, gs; int ldtr, iomap; };
このコードはTSS(タスク状態セグメント)というものでこれをつかってレジスタの内容をメモリに書き込みます。TSSはintが26個集まったもので全部で104バイトです。
1行目はタスクの設定に関する内容、2行目は3bitのレジスタ、3行目は16bitのレジスタです。4行目のldtrとiomapは1行目と同じくタスク設定に関する部分です。
タスクスイッチしてみる
ということでタスクスイッチをしてみます。タスクAとタスクBを準備してAからBへ切り替えます。まずはTSSを二つ作る必要があります。
メインクラスにTSSの呼び出しを書き、ldtrとiomapに適当な値を代入します。
// 呼び出し struct TSS32 tss_a, tss_b; // tss_aに値を代入 tss_a.ldtr = 0; tss_a.iomap = 0x40000000; // tss_bに値を代入 tss_b.ldtr = 0; tss_b.iomap = 0x40000000; // GDTに登録(tss_aをgdtの3番目、tss_bをgdtの4番目に設定) struct SEGMENT_DISCRITPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT; set_segmdesc(gdt + 3, 103, (int) &tss_a, AR_TSS32); set_segmdesc(gdt + 4, 103, (int) &tss_b, AR_TSS32);
ここまで出来たら実際に切り替えをします。
続きは次回
マルチタスク-1はここでまだ半分です。なかなか手強いので15日目は2つにわけたいと思いますので今回はここまでです。