map.pdf の Running a process のメモ
以下にて。メモということでご容赦願います。
Running a process
- scheduler は state 属性が RUNNABLE なプロセスを探すんですが、現時点ではそれは userinit で生成されるプロセスなオブジェクト
- switchuvm 手続きは target process な page table を使って start することを hardware に教えるよ、とあります
- たしかにプロセス構造体の pgdir 属性を cr3 にロードしてますね
- gdt もリセットされてますがスルー? って思ったら次の文で云々な模様
- 以下な記述は確認が必要
// Setup TSS cpu->gdt[SEG_TSS] = SEG16(STS_T32A, &cpu->ts, sizeof(cpu->ts)-1, 0); cpu->gdt[SEG_TSS].s = 0; cpu->ts.ss0 = SEG_KDATA << 3; cpu->ts.esp0 = (uint)proc->kstack + KSTACKSIZE; ltr(SEG_TSS << 3);
-
- 記述として_switchuvm also creates a new task state segment SEG_TSS that instructs the hardware to handle an interrupt by returning to kernel mode with ss and esp set to SEG_KDATA<<3 and (uint)proc->kstack+KSTACKSIZE, the top of this process’s kernel stack._というものがありますが、stack segment register が SEG_KDATA<<3 に、というのがナニ
- SEG_* は proc.h で定義されてて以下らしい
// Segments in proc->gdt. // Also known to bootasm.S and trapasm.S #define SEG_KCODE 1 // kernel code #define SEG_KDATA 2 // kernel data+stack #define SEG_KCPU 3 // kernel per-cpu data #define SEG_UCODE 4 // user code #define SEG_UDATA 5 // user data+stack #define SEG_TSS 6 // this process's task state #define NSEGS 7
-
-
- bootasm.S と trapasm.S で云々というコメントがありますね
-
- TSS って何かと思い調べてみましたら Task State Segment というものらしく詳解 Linux Kernel なソレによれば以下とのこと
- 80x86 CPU がユーザモードからカーネルモードに切り替わるとき、カーネルモードスタックのアドレスを TSS から読み込みますという記述があるけど、xv6 なコードとは整合してない風に見えます
- とりあえず流して後で SEG16 なマクロとか確認の方向
- 80x86 CPU がユーザモードからカーネルモードに切り替わるとき、カーネルモードスタックのアドレスを TSS から読み込みますという記述があるけど、xv6 なコードとは整合してない風に見えます
- scheduler はプロセスの state 属性を RUNNING にして swtch 手続きを呼び出す
- _to perform a context switch to the target process's kernel thread_って記述があります
- 呼び出し記述は _swtch(&cpu->scheduler, proc->context);_ となってます
- swtch 手続きは swtch.S です
- 定義が以下
# Context switch # # void swtch(struct context **old, struct context *new); # # Save current register context in old # and then load register context from new.
-
-
- old は current context なのか (値が変更される可能性あり?
- new は新たに load される云々とありますね
-
.globl swtch swtch: movl 4(%esp), %eax movl 8(%esp), %edx
-
-
- 引数を順に eax と edx レジスタに格納
- old が eax で new が edx で良いのかどうか
-
# Save old callee-save registers pushl %ebp pushl %ebx pushl %esi pushl %edi
-
-
- レジスタ退避 (呼び出される側?
-
# Switch stacks movl %esp, (%eax) movl %edx, %esp
-
-
- current なスタックを eax に退避して edx を esp に load
-
# Load new callee-save registers popl %edi popl %esi popl %ebx popl %ebp ret
-
-
- switch した stack からレジスタを復帰して ret
- ret って何するんでしたっけ
- あら? struct context って stack なんスか?
-
とゆーことで
深みにハマらないうちに手を止めます。と言いつつちょっと確認。
ret 命令は上記の状態で ip が stack のてっぺんにあるこを前提にしていそげ。あー、cpu->scheduler とか proc->context なのか。ちなみに cpu->scheduler も context 構造体のポインタになってたりして。
struct context 型
うをを、コメントまで引用してやれ。
// Saved registers for kernel context switches. // Don't need to save all the segment registers (%cs, etc), // because they are constant across kernel contexts. // Don't need to save %eax, %ecx, %edx, because the // x86 convention is that the caller has saved them. // Contexts are stored at the bottom of the stack they // describe; the stack pointer is the address of the context. // The layout of the context matches the layout of the stack in swtch.S // at the "Switch stacks" comment. Switch doesn't save eip explicitly, // but it is on the stack and allocproc() manipulates it. struct context { uint edi; uint esi; uint ebx; uint ebp; uint eip; };
もうそのまんまじゃないスか。てーかこいつらどこで設定されているのか。というのがアレなんですが、直前の Process creation から再確認しないとダメなことに気づいていたりしています。
forkret とか trapret のあたりがアレ? というか userinit から呼ばれる allocproc なのか。このへん? (@allocproc)
sp -= sizeof *p->context; p->context = (struct context*)sp; memset(p->context, 0, sizeof *p->context); p->context->eip = (uint)forkret; return p;
ああ、やっぱ Process creation からもっかい見た方が良さげ。
Process creation
以下な記述があるよorz
Now allocproc must set up the new process’s kernel stack.
とほほ。やっぱ allocproc って特別なのだな。