φ(・・*)ゞ ウーン 両者の違いをよく忘れるのでorz
mdcpuもmycpuも今動いているcpuが持ってるデータにアクセスするために使うのは良いとして、mdcpuのmdは多分machine dependentとかそんな意味ですよね。ということで、mdcpuはアーキテクチャ依存な情報、mycpuはアーキテクチャ非依存なデータを取得するという使い分けでしょうか。
mdcpuはsys/platform/pc64/include/globaldata.hで定義されています。
#define mdcpu ((struct mdglobaldata *)_get_mycpu())
mycpuのほうはsys/platform/pc64/include/thread.hで定義されています。
#define mycpu _get_mycpu()
それでmdcpu、mycpuともに_get_cpu()を呼び出しているのでこれを見てみます。
実体はsys/platform/pc64/include/thread.hにあります。
struct globaldata; extern int __mycpu__dummy; static __inline struct globaldata * _get_mycpu(void) { struct globaldata *gd; __asm ("movq %%gs:globaldata,%0" : "=r" (gd) : "m"(__mycpu__dummy)); return(gd); }
内容としては見ての通りで%%gs:globaldataにあるglobaldata構造体のデータが返ります。
mdcpuの場合はstruct mdglobaldata*にキャストしてますがこれはmdglobaldata構造体がこのようになっているのでうまいこと行くわけですね。
struct mdglobaldata { struct globaldata mi; struct user_segment_descriptor gd_common_tssd; ~略~
さて、変数としてのglobaldataはどこで定義しているかというとsys/platform/pc64/x86_64/global.sで定義されています。
.globl globaldata .set globaldata,0 /* * Define layout of the global data. On SMP this lives in * the per-cpu address space, otherwise it's in the data segment. */ .globl gd_curthread, gd_npxthread, gd_reqflags, gd_common_tss .set gd_curthread,globaldata + GD_CURTHREAD .set gd_npxthread,globaldata + GD_NPXTHREAD .set gd_reqflags,globaldata + GD_REQFLAGS .set gd_common_tss,globaldata + GD_COMMON_TSS .globl gd_common_tssd, gd_tss_gdt .set gd_common_tssd,globaldata + GD_COMMON_TSSD .set gd_tss_gdt,globaldata + GD_TSS_GDT .globl gd_currentldt .set gd_currentldt,globaldata + GD_CURRENTLDT .globl gd_fpu_lock, gd_savefpu .set gd_fpu_lock, globaldata + GD_FPU_LOCK .set gd_savefpu, globaldata + GD_SAVEFPU
これでglobaldataがどこで宣言されているかは分かったので次はどこでデータを設定しているかですね。
mycpu、mdcpuともにプロセスが動いているcpuが持っているデータなのでデータを更新するとすればコンテキストスイッチのタイミングですよねーということで探してみるとコンテキストスイッチをしているのはsys/platform/pc64/x86_64/swtch.sでした。
一口にコンテキストスイッチと言ってもいくつか種類があったけど通常ではこれが使われるっぽいです。
ENTRY(cpu_lwkt_switch) pushq %rbp /* JG note: GDB hacked to locate ebp rel to td_sp */ pushq %rbx movq PCPU(curthread),%rbx /* becomes old thread in restore */ pushq %r12 pushq %r13 pushq %r14 pushq %r15 pushfq cli #if 1 /* * Save the FP state if we have used the FP. Note that calling * npxsave will NULL out PCPU(npxthread). * * We have to deal with the FP state for LWKT threads in case they * happen to get preempted or block while doing an optimized * bzero/bcopy/memcpy. */ cmpq %rbx,PCPU(npxthread) jne 1f movq %rdi,%r12 /* save %rdi. %r12 is callee-saved */ movq TD_SAVEFPU(%rbx),%rdi call npxsave /* do it in a big C function */ movq %r12,%rdi /* restore %rdi */ 1: #endif movq %rdi,%rax /* switch to this thread */ pushq $cpu_lwkt_restore movq %rsp,TD_SP(%rbx) /* * %rax contains new thread, %rbx contains old thread. */ movq %rax,PCPU(curthread) movq TD_SP(%rax),%rsp ret
このファイルの中でそれっぽいのはこの辺ですね。
movq %rdi,%rax /* switch to this thread */
ここのcpu_lwkt_switchは関数でcのプロトタイプはsys/sys/proc.hでこのようになってます。
thread_t cpu_lwkt_switch (struct thread *);
x86_64のABIだとrdiレジスタに引数がセットされるのでcpu_lwkt_switch()の引数は%rdiにセットされていて、これは次に動かしたいスレッドの構造体へのポインタとなります。そして関数内でデータが%rdiから%raxにコピーされて、カレントスレッドの構造体を示すcurthreadに次に動くスレッドのデータがセットされます。
movq %rdi,%rax /* switch to this thread */ pushq $cpu_lwkt_restore movq %rsp,TD_SP(%rbx) /* * %rax contains new thread, %rbx contains old thread. */ movq %rax,PCPU(curthread) movq TD_SP(%rax),%rsp
この関数内でPCPUというマクロが出てきていますがこれはcpu/x86_64/include/asmacros.hで定義されています。
/* * Access per-CPU data. */ #define PCPU(member) %gs:gd_ ## member #define PCPU_E8(member,idx) %gs:gd_ ## member(,idx,8) #define PCPU_ADDR(member, reg) \\ movq %gs:PC_PRVSPACE, reg ; \\ addq $PC_ ## member, reg
これは単純なマクロですね
movq %rax,PCPU(curthread)
↑だったら↓のように展開されるだけです。
movq %rax,%gs:gd_curthread
ここでglobaldataに戻ってこの部分を見てみます。
.set gd_curthread,globaldata + GD_CURTHREAD
GD_CURTHREADはplatform/pc64/x86_64/genassym.cで以下のように定義されてます。
ASSYM(GD_CURTHREAD, offsetof(struct mdglobaldata, mi.gd_curthread));
ここまで見てみるとなるほどな~ってなりますね。