15日目はマルチタスク(タスクスイッチ)をやりました。
マルチタスクp290~310
- CPUは複数のタスクを同時に実行することができない。タスクをそれぞれ少しずつ実施すること実現できる。
- そのための仕組みがタスクスイッチ
- TR(task register)に現在のタスク番号を格納する
これを短い期間で繰り返している。
タスクスイッチ
保存するレジスタ等の内容
TSS(task status segment)という種類のセグメントを利用します。 int型のメンバが26個ありますので、保存するレジスタ等の情報は全てで26*4=104バイトの領域が必要です。
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; };
こちらをGDTに登録し、タスクスイッチのたびにロード、セーブという具合です。
- 1段目
int backlink, esp0, ss0, esp1, ss1, esp2, ss2, cr3;
はレジスタの情報ではありません
JMP命令とEIPレジスタ
- 次の命令の実行場所はEIPレジスタに保持して管理されている
- JMP命令というのは実施
MOV EIP,<JUMP先アドレス>
と同じ意味- ただしアセンブラでEIPに直接代入できない。
- だから素直にJMP命令を使いましょう。
- ただしアセンブラでEIPに直接代入できない。
JUMP命令のnearモードとfarモード
- 命令の読み込み位置はcsとeipによって決まる
- リアルモード時はcs*16+eipで表現。
- csはセグメントのベースアドレス、eipはオフセットアドレスの意味になる。
- プロテクトモード時のcsの挙動の理解が曖昧。
実行イメージ
taskA実行中にtaskBに切り替える例で説明されています。 1.TSSをtaskA,taskBで用意
struct TSS32 tss_a, tss_b;
2.tss_bのeipにタスクとなるプログラムを設定
(関数ポインタは初見?な気がするが特に説明なし?)
void task_b_main(void) { for (;;) { io_hlt(); }//HLT命令。何もしない。 }
tss_b.eip = (int) &task_b_main;
3.GDTにTSSを登録設定
set_segmdesc(gdt + 3, 103, (int) &tss_a, AR_TSS32); set_segmdesc(gdt + 4, 103, (int) &tss_b, AR_TSS32);
4.10秒のカウント表示時に同時にタスクスイッチ命令
} else if (i == 10) { /* 10秒タイマ */ putfonts8_asc_sht(sht_back, 0, 64, COL8_FFFFFF, COL8_008484, "10[sec]", 7); taskswitch4(); //コレ!!!!!
5.アセンブリに定義したタスクスイッチ関数を実行。 ここでfarモードでジャンプ。
_taskswitch4: ; void taskswitch4(void); JMP 4*8:0 RET
コレで3.で登録したGDT+4(task_b)の位置にJMP。この時、ジャンプ先のセグメントが実行可能セグメントではなく、 TSSなのでCPUはEIPやCSを書き換えずにタスクスイッチ命令だと解釈し、TSSで指定したタスクに変わるらしい。
※本章では以後決め打ちになっているタスク名やアドレスは関数の引数にして汎用的なタスクスイッチ処理に作り変えられる。
動画
上記のプログラムを動かした動画。10秒経過の次の瞬間にHLT命令が実行されマウス操作が受け付けられなくなっている。*1
感想
CPUがマルチコアだとどうなる?とか、Javaでマルチスレッドなプログラミングを行った時にCPUレベルではどのように動く?などまだまだイメージできないことは多いですが、
- マルチコア
- マルチタスク
- マルチスレッド
この辺の違いを意識していこう。
- 作者: 川合秀実
- 出版社/メーカー: 毎日コミュニケーションズ
- 発売日: 2006/03/01
- メディア: 単行本
- 購入: 36人 クリック: 735回
- この商品を含むブログ (299件) を見る
*1:マウスの割り込み操作は多分受け付けているが描画処理が実行されていない。