TSSを使用しないコンテキストスイッチ
30日本とか、はじめて読む486とかで紹介されているコンテキストスイッチ(タスクスイッチ)は、TSS(タスク状態セグメント)を利用した方法である。
しかしここ2週間、TSSを用いたコンテキストスイッチがどうもうまく行かないので、別の方法を試みた・・・というよりは、迷っている間に自前で用意できそうな気がした。
ちなみに、LinuxもTSSを用いたコンテキストスイッチを行っていないことから(アーキテクチャへの依存性などを考慮しているため)、TSSを使わないコンテキストスイッチで実装することは、よい勉強になるのではないと思う。
で、実装できたのでここで書いてみる。
TSSを用いないコンテキストスイッチを行う方法は、以下のようになる。
- 前タスクのレジスタ(汎用レジスタ、EFLAGS)の退避
- 前タスクのスタックポインタ(ESP)の退避
- 前タスクのプログラムカウンタ(EIP)の退避
- 次タスクのスタックポインタ(ESP)の設定
- 次タスクのプログラムカウンタ(EIP)へジャンプ
- 次タスクのレジスタ(汎用レジスタ)の設定
具体的なソースコードを示すと、次のようになる。
ソースコードはintel記法とする。
# 前タスクのレジスタの退避 pushad pushfd mov [前タスクのスタックポインタの保存先], esp mov [前タスクのプログラムカウンタの保存先], restore # 次タスクのレジスタの設定 mov esp, [次タスクのスタックポインタの保存先] jmp [次タスクのプログラムカウンタの保存先] restore: popfd popad
基本的にスタックにレジスタを保存させることで、コンテキストスイッチを実現している。
ここで気をつけなくてはならないのは、前タスクのプログラムカウンタの保存先をrestoreラベル(jmp命令の次のアドレス)にしているところである。
このようにすることで、次に前タスクに処理が戻ったときに、popfdからスタートさせることができる。
まだ不十分のところがあるが、githubに自作OSの最新のソースコードを置いた。
https://github.com/nutti/Educational-Operating-System/blob/master/src/asm_util.s
switch_task_2というラベルが、今回話題にした部分である。
実行結果以下に示す。・・・といっても画像なので、マルチタスクが動作しているようには見えないが。
[参考資料]
・Linuxカーネルのソースコード(バージョン2.6.15のinclude/asm-i386/system.hにあるswitch_toマクロ)
・http://d.hatena.ne.jp/naoya/20070924/1190653790