swtch.S swtch

トップページ
jupiteroak.hatenablog.com


swtch.S
https://github.com/mit-pdos/xv6-public/blob/master/swtch.S

.globl swtch
swtch:
 
movl 4(%esp), %eax
movl 8(%esp), %edx
 
pushl %ebp
pushl %ebx
pushl %esi
pushl %edi

movl %esp, (%eax)
movl %edx, %esp

popl %edi
popl %esi
popl %ebx
popl %ebp
 
ret

swtchサブルーチンは、ハードウェアコンテキストの切り替えを行います。
ここでは、①scheduler関数から呼び出されるswtchサブルーチンと②sched関数から呼び出されるswtchサブルーチン の2つのケースについて、具体的に説明します。

目次

①scheduler関数から呼び出されるswtchサブルーチン

scheduler関数から呼び出されるswtchサブルーチン(swtch(&(c->scheduler), p->context):第1引数が&(c->scheduler)、第2引数がp->context)について説明します。
scheduler関数から呼び出されるswtchサブルーチンは、現在実行されているカーネルスレッドにおけるハードウェアコンテキストをカーネルスレッドのスタックに退避させ、切り替え先となるプロセスのカーネルスタックからハードウェアコンテキストを復帰させます。

swtch(&(c->scheduler), p->context)を呼び出した直後のスタックの状態

アドレス 格納されている値
... ...
下位アドレス側
... ...
swtchサブルーチンのコールスタックにおける先頭アドレス
scheduler関数のコールスタックにおける終端アドレス リターンアドレスの値
switchサブルーチンの第1引数の値
switchサブルーチンの第2引数の値
... ...
scheduler関数のコールスタックにおける先頭アドレス mpmain関数のコールスタックにおける先頭アドレスの値
mpmain関数のコールスタックにおける終端アドレス リターンアドレスの値
... ...
mpmain関数のコールスタックにおける先頭アドレス ...
... ...
上位アドレス側
... ...

また、swtch(&(c->scheduler), p->context)を呼び出した直後のespレジスタには、scheduler関数のコールスタックにおける終端アドレスの値がセットされています(スタックポインタの値は、scheduler関数のコールスタックにおける終端アドレスの値になっています)。

第1引数の値をeaxレジスタにセットし、第2引数の値をedxレジスタにセットする

movl 4(%esp), %eax
movl 8(%esp), %edx


movl 4(%esp), %eax
movl命令によって、switchサブルーチンの第1引数の値&(c->scheduler)をeaxレジスタにセットします。
&(c->scheduler)は、context構造体へのポインタschedulerを指定するアドレスです。
ポインタschedulerは、カーネルスレッドにおいて使用されるスタックのトップアドレスの値を保存するために使用されます。
movl 4(%esp), %eaxは、espレジスタ(スタックポインタ)が指定するアドレスから上位アドレス側へ4バイト進めたメモリ領域にある値を、eaxレジスタにセットします。
espレジスタ(スタックポインタ)が指定するアドレスはscheduler関数のコールスタックにおける終端アドレスです(espレジスタの値(スタックポインタの値)は、scheduler関数のコールスタックにおける終端アドレスの値です)。
よって、scheduler関数のコールスタックにおける終端アドレスから上位アドレス側へ4バイト進めたメモリ領域にある値は、switch関数の第1引数の値&(c->scheduler)となります。


movl 8(%esp), %edx
movl命令によって、switchサブルーチンの第2引数の値p->contextをedxレジスタにセットします。
p->contextは、切り替え先プロセスにおけるカーネルスタックのトップアドレスの値です。
movl 8(%esp), %edxは、espレジスタ(スタックポインタ)が指定するアドレスから上位アドレス側へ8バイト進めたメモリ領域にある値を、edxレジスタにセットします。
espレジスタ(スタックポインタ)が指定するアドレスはscheduler関数のコールスタックにおける終端アドレスです(espレジスタの値(スタックポインタの値)は、scheduler関数のコールスタックにおける終端アドレスの値です)。
よって、scheduler関数のコールスタックにおける終端アドレスから上位アドレス側へ8バイト進めたメモリ領域にある値は、switch関数の第2引数の値p->contextとなります。

カーネルスレッドのハードウェアコンテキストをカーネルスレッドのスタックに退避させる

pushl %ebp
pushl %ebx
pushl %esi
pushl %edi

pushl命令によって、ebpレジスタの値、ebxレジスタの値、esiレジスタの値、ediレジスタの値をカーネルスレッドにおいて使用されるスタックに退避させます。
ebpレジスタには、swtchサブルーチンの呼び出し元であるscheduler関数が使用するコールスタックの先頭アドレス値が格納されています。
pushl %ebpは、scheduler関数が使用するコールスタックの先頭アドレス値を、swtchサブルーチンが使用するコールスタックに退避させます。

カーネルスレッドのスタックからプロセスのカーネルスタックへ切り替える

movl %esp, (%eax)
movl %edx, %esp

現在使用しているカーネルスレッドのスタックから切り替え先プロセスのカーネルスタックへ切り替えます。
espレジスタ(スタックポインタ)が指定するアドレスを変更することで、スタックを切り替えることができます。


movl %esp, (%eax)
movl命令によって、カーネルスレッドのスタックのトップアドレスをc->schedulerに格納します。
movl %esp, (%eax)は、espレジスタの値を、eaxレジスタにセットされたアドレスが指定するメモリ領域に格納します。
espレジスタの値は、現在実行されているカーネルスレッドにおけるスタックのトップアドレスの値です。
eaxレジスタにセットされたアドレスは、context構造体へのポインタschedulerを指定するアドレス&(c->scheduler)です。
そのため、movl %esp, (%eax)は、カーネルスレッドにおけるカーネルスタックのトップアドレスをc->scheduler(&(c->scheduler)が指定するメモリ領域)に格納します。


movl %edx, %esp
movl命令によって、切り替え先プロセスにおけるカーネルスタックのトップアドレスの値を、espレジスタにセットします。
edxレジスタには、p->contextの値(プロセスにおけるカーネルスタックのトップアドレス値:allocproc関数で用意される)がセットされています。
movl命令が実行された後は、プロセスが持つカーネルスタックが利用されます。

プロセスのカーネルスタックからハードウェアコンテキストを復帰させる

popl %edi
popl %esi
popl %ebx
popl %ebp

popl命令によって、ediレジスタの値、esiレジスタの値、ebxレジスタの値、ebpレジスタの値をプロセスのカーネルスタックから復帰させます。

切り替え先プロセスのハードウェアコンテキストから処理を開始する

ret

ret命令によって、プロセスのカーネルスタックに退避させてあるリターンアドレスをeipレジスタに復帰させます。
これにより、swtchサブルーチンを呼び出したプロセッサが、切り替え先のプロセスを実行します。

②sched関数から呼び出されるswtchサブルーチン

sched関数から呼び出されるswtchサブルーチン(swtch(&p->context, mycpu()->scheduler):第1引数が&p->context、第2引数がmycpu()->scheduler)について説明します。
sched関数から呼び出されるswtchサブルーチンは、現在実行されているプロセスにおけるハードウェアコンテキストをそのプロセスのカーネルスタックに退避させ、切り替え先となるカーネルスレッドのスタックからハードウェアコンテキストを復帰させます。