map.pdf の Running a process のメモ (2)
e*lipse がメモリ大量消費するお陰で披露困憊。なんとか頑張ってみます。
とりあえず Process creation の項から
ざくっと再度確認。ええと、struct proc の kstack な属性についての説明が意味分からんぞ。_for example in a system call_という記述がありますな。あ、_xv6 allocates one kernel stack for each process._という記述もある。
IO wait とか system call からの復帰アドレスとかを持っとく、って理解で良いのかな。
userinit 手続き
属性の説明はスルーして userinit のあたりの記述を確認。
allocproc の仕事は_to allocate a slot (a struct proc) in the process table and to initialize the parts of the process’s state required for its kernel thread to execute._とあります。
あ、と思ったら kstack 属性をもごもごしてます。kstack って何だ。ってマンガを引用したのは 8/6 なエントリらしい。
Here is the state of the new process’s kernel stack: ---------- <-- top of new process’s kernel stack | esp | | ... | | eip | | ... | | edi | <-- p->tf (new proc’s user registers) | trapret | <-- address forkret will return to | eip | | ... | | edi | <-- p->context (new proc’s kernel registers) | | | (empty) | | | ---------- <-- p->kstack
allocproc 手続きでは上記 p->kstack の領域を確保して上のマンガの通り、p->tf が指す領域、p->context が指す領域、を設定してたり trapret を stack に格納してたりしますね。あと、p->context->eip は forkret の開始アドレスが格納されるのかな。
上記マンガによれば trapframe が user 側で context が kernel 側なのかどうか。
ということで確認したところでは Process creation は userinit 手続きの中身の解説になっているようです。セグメントレジスタ云々なあたりは若干微妙でしたが、なんとか理解ができているのかどうか。
Running a process
mpmain の中で呼ばれている scheduler 手続きに関する解説なのかどうか。task state segment に関する記述がちょろっと出てきますが、別途再確認する模様。
あ、あと swtch ですが callee 側の記述が以下。
swtch(&cpu->scheduler, proc->context);
で、swtch の中ですが、まず引数をレジスタに確保して
swtch: movl 4(%esp), %eax movl 8(%esp), %edx
次にレジスタを stack に格納。
# Save old callee-save registers pushl %ebp pushl %ebx pushl %esi pushl %edi
stack を switch らしい
# Switch stacks movl %esp, (%eax) movl %edx, %esp
これ、元が cpu->scheduler に格納されて proc->context が新たな stack になる模様。で、そこから pop して
# Load new callee-save registers popl %edi popl %esi popl %ebx popl %ebp
ret します。
ret
で、上述の通り、proc->context->eip は forkret なアドレスが入ってて、ret でそれが eip にセットされるはず。このあたりの記述も mem.pdf にありますね。
forkret は以下の通り
forkret(void) { // Still holding ptable.lock from scheduler. release(&ptable.lock); // Return to "caller", actually trapret (see allocproc). }
なので再度 ret 命令が実行されるんですが、eip の次は trapret の関数ポインタが設定されております。ので、trapret に jmp する、ということなのか。で、trapret の手続き定義が以下。
# Return falls through to trapret... .globl trapret trapret: popal popl %gs popl %fs popl %es popl %ds addl $0x8, %esp # trapno and errcode iret
popal してtrapframe なソレを pop して iret されておる。てか、なんでここまで面倒なことをしないといけないのかと小一時間。。
このあたりが佳境だとは思うのですが意味不明。カギは trap frame ッスか? んーと proc.c の userinit 手続きの以下あたりがそうなの? と言いつつ trapframe 構造体の定義確認してびっくり。上の命令がそのまんま通用する形で定義されとるやんけ。
なるほど
ここまでは腑にオチました。が、次の _%eip holds zero and %esp holds 4096. These are virtual addresses in the process's address space._ というあたりがナニ。もうちょいですが、ここで中断。
再開
allocuvm 手続きが PTE を用意する云々な記述がありますが、allocuvm 手続きを呼び出すのは相当限定された手続き。ええと、って思っていたらどうも load される initcode.S なソレから exec な system call が呼び出されてて、exec から allocuvm が起動されているのかどうか。
あ、initcode の page table は inituvm でセットされているのか。で、initcode が exec するのは init という実行ファイルなのかな。init.c というソースファイルがありますね。
Exercises
- swtch で breakpoint 設定して forkret とか trapret のあたりをもごもごしなさい、とあります
- 実際の OS でどうやってメモリサイズを確定させてるかを見れ
とのこと。明日以降で確認してみます。