アダルト・ヴァイオリン May the force be with you..

2007年5月29日

Linuxのタスクスイッチマクロ switch_to

Filed under: Weblog — @ 11:54 PM

カーネルスケジューラのキーが、switch_toというマクロである。
このマクロの中で、スタックを切り替えることで、タスクが切り替わる。

このマクロ、引数を3個取るのだが、この説明をまともにしている書籍が(先日紹介したものを含め)ない。

i386では、このマクロは次のように定義されている。
問題は、3つめのlast引数である。実は、この引数は、本来必要はないものだ。多くの解説本では、もっともらしい言い訳を創出しているが、アセンブラに対して中で呼び出す__switch_toの入出力を明示的に示すためだけにあるのである。

#define switch_to(prev,next,last) do {                                  \
unsigned long esi,edi;                                          \
asm volatile("pushfl\n\t"                                       \
"pushl %%ebp\n\t"                                  \
"movl %%esp,%0\n\t"        /* save ESP */          \
"movl %5,%%esp\n\t"        /* restore ESP */       \
"movl $1f,%1\n\t"          /* save EIP */          \
"pushl %6\n\t"             /* restore EIP */       \
"jmp __switch_to\n"                                \
"1:\t"                                             \
"popl %%ebp\n\t"                                   \
"popfl"                                            \
:"=m" (prev->thread.esp),"=m" (prev->thread.eip),  \
"=a" (last),"=S" (esi),"=D" (edi)                 \
:"m" (next->thread.esp),"m" (next->thread.eip),    \
"2" (prev), "d" (next));                          \
} while (0)

colinuxのカーネルビルド環境を作ったついでに、3つ目の引数を使わないマクロに変更し、動作させてみる。変更後のマクロを次に示す。

#define switch_to(prev,next) do {                                       \
unsigned long esi,edi;                                          \
asm volatile("pushfl\n\t"                                       \
"pushl %%ebp\n\t"                                  \
"movl %%esp,%0\n\t"        /* save ESP */          \
"movl %5,%%esp\n\t"        /* restore ESP */       \
"movl $1f,%1\n\t"          /* save EIP */          \
"pushl %6\n\t"             /* restore EIP */       \
"jmp __switch_to\n"                                \
"1:\t"                                             \
"popl %%ebp\n\t"                                   \
"popfl"                                            \
:"=m" (prev->thread.esp),"=m" (prev->thread.eip),  \
"=a" (prev),"=S" (esi),"=D" (edi)                 \
:"m" (next->thread.esp),"m" (next->thread.eip),    \
"2" (prev), "d" (next));                          \
} while (0)

当然だが、sched.cのswitch_toの呼び出しも第3引数を削る。

こうやって作ったvmlinuxは全く普通に動作することからも、第3の引数が不要であったことが確かめられる。

上の変更後マクロのポイントは、prevを入力にも出力にも利用している点だ。これによって、FASTCALLの__switch_toへの引数ならびに返り値として、EAXを利用するコードとなって、所期の目的が果たせるのだ。

少なくとも、i386系列のアーキテクチャで第3の引数がなくて困るものはないと思うのだが・・・?

Powered by WordPress