mpmain()scheduler()swtch()とプロセス切り替えできるまで、 エミュレータの開発が進んだ。 xv6のコードproc.cの334行目sti命令をずっと発行していて、どうやらループに入っているようなので、 ここでスケジューリング、コンテキストスイッチ周りをまとめたい。 切り替え先のプロセスは、切り替えられた直後forkret()iinit()関数内にて、IOを行っている。 ここで、スリープ状態に入り、割り込みを待っているようだ。

proc構造体

xv6では各プロセスはproc構造体で管理される。 contextには、コンテキストスイッチのためのレジスタを格納する。 CSなどセグメントレジスタは、プロセス間で共通なので、保存する必要がない。 EAX、ECS、EDXなどは関数呼び出し時に自動的にスタックに保存されるため、contextに含めない。 最初のユーザプロセスは、userinit関数とallocproc関数で作成する。 proc構造体のコメントには、最初のユーザプロセスのデータをメモしておく。 トラップフレームは、ハードウェアとtrapasm.Sによりスタック上に積まれるもので、 trap関数へと引き渡される。 forkret関数のreturn先は、trapretに設定する。

struct proc {
  uint sz;                     // Size of process memory (bytes)
  pde_t* pgdir;                // Page table
  char *kstack;                // このプロセスのカーネルスタックの底
  enum procstate state;        // プロセスの状態:EMBRYO
  int pid;                     // PID:1
  struct proc *parent;         // 親プロセス:NULL
  struct trapframe *tf;        // トラップフレーム
  struct context *context;     // このプロセスのコンテキスト
  void *chan;                  // If non-zero, sleeping on chan
  int killed;                  // If non-zero, have been killed
  struct file *ofile[NOFILE];  // Open files
  struct inode *cwd;           // カレントディレクトリ:/
  char name[16];               // プロセス名:initcode
};

struct context {
  uint edi;
  uint esi;
  uint ebx;
  uint ebp;
  uint eip; // forkret
};

struct trapframe {
  // registers as pushed by pusha
  uint edi, esi, ebp, oesp, ebx, edx, ecx, eax;

  // rest of trap frame
  ushort gs, padding1, fs, padding2, es, padding3, ds, padding4;
  uint trapno;

  // below here defined by x86 hardware
  uint err;
  uint eip;
  ushort cs;
  ushort padding5;
  uint eflags;

  // below here only when crossing rings, such as from user to kernel
  uint esp;
  ushort ss;
  ushort padding6;
};

struct {
  struct spinlock lock;
  struct proc proc[NPROC];
} ptable;

カーネルスタック

proc->kstackの中身を図示しておく。 swtchでは、espproc->contextとして、edi、esi、ebx、ebpをpopする。 そして、最後にret命令で、eipを復元する。

(lower address)
 ------------------- 
|                   | <= proc->kstack
|                   |
 ------------------- 
 ------------------- 
| edi               | <= proc->context
| esi               |
| ebx               |
| ebp               |
| eip = forkret     |
 ------------------- 
 ------------------- 
|                   | <= trapret(関数ポインタ)
 ------------------- 
 ------------------- 
|                   | <= proc->tf
 ------------------- 
                      <= proc->kstack + KSTACKSIZE(4096)
(upper address)

|—————-| | |

参考