Upgrade to Pro — share decks privately, control downloads, hide ads and more …

xv6 chapter5 first

xv6 chapter5 first

Tomoya Ishizaki

May 20, 2019
Tweet

More Decks by Tomoya Ishizaki

Other Decks in Programming

Transcript

  1. 今日のコード • コード:コンテキストスイッチ ◦ struct context ◦ void swtch(struct context

    **old, struct context *new) • コード:スケジューリング ◦ void trap(struct trapframe *tf) ◦ void yield(void) ◦ void sched(void) ◦ void scheduler(void) • コード:mycpu & myproc ◦ struct cpu ◦ struct cpu* mycpu(void) ◦ struct proc* myproc(void)
  2. どうやってコンテキストスイッチを行うか • 低レイヤでは, 二種類の スイッチを行っている • 各CPUは, 固有の隔離された スケジューラスレッドをもつ •

    ユーザープロセスが 他のユーザープロセスに 直接スイッチすることはない ユーザープロセスからスケジューラへ スケジューラからユーザープロセスへ
  3. 今日のコード • コード:コンテキストスイッチ ◦ struct context ◦ void swtch(struct context

    **old, struct context *new) • コード:スケジューリング ◦ void trap(struct trapframe *tf) ◦ void yield(void) ◦ void sched(void) ◦ void scheduler(void) • コード:mycpu & myproc ◦ struct cpu ◦ struct cpu* mycpu(void) ◦ struct proc* myproc(void)
  4. struct context • 汎用レジスタの一部を保持する ◦ %edi, %esi, %ebx, %ebp •

    %eipも保持する ◦ swtch()で明示的に操作している わけではないが, allocproc()内で 保存している ◦ 詳しくは1章 https://github.com/mit-pdos/xv6-public/blob/master/proc.h#L27
  5. void swtch(struct context **old, struct context *new) • contextを使ってレジスタを切り替える •

    contextの保存と復元 ◦ old(古いcontext)を保存 ◦ new(新しいcontext)を復元 • アセンブラで実装されている https://github.com/mit-pdos/xv6-public/blob/master/swtch.S
  6. void swtch(struct context **old, struct context *new) • **old, *new(引数)とswtchのリターンアドレス(戻り先)が

    カーネルスタックに積んである • *newは移り先のプロセスのカーネルスタックを指している https://github.com/mit-pdos/xv6-public/blob/master/swtch.S ... %eip(戻り先) %ebp %ebx %esi %edi ... ... *new **old %eip(戻り先) *new oldのカーネルスタック newのカーネルスタック %esp
  7. void swtch(struct context **old, struct context *new) • %eax が

    **old を指すようになる • %edx が *new を指すようになる https://github.com/mit-pdos/xv6-public/blob/master/swtch.S ... %eip(戻り先) %ebp %ebx %esi %edi ... ... *new **old %eip(戻り先) %edx oldのカーネルスタック newのカーネルスタック %eax %edx %esp
  8. void swtch(struct context **old, struct context *new) • 現在のレジスタをスタックに保存する https://github.com/mit-pdos/xv6-public/blob/master/swtch.S

    ... %eip(戻り先) %ebp %ebx %esi %edi ... ... *new **old %eip(戻り先) %ebp %ebx %esi %edi %edx oldのカーネルスタック newのカーネルスタック %eax %edx %esp
  9. void swtch(struct context **old, struct context *new) • *old(%eaxの指すところ)が %esp

    を指すようになる https://github.com/mit-pdos/xv6-public/blob/master/swtch.S ... %eip(戻り先) %ebp %ebx %esi %edi ... ... *new **old %eip(戻り先) %ebp %ebx %esi %edi %edx oldのカーネルスタック newのカーネルスタック %esp %edx *old %eax
  10. void swtch(struct context **old, struct context *new) • %esp が

    %edx を指すようになる(スタックが切り替わる) https://github.com/mit-pdos/xv6-public/blob/master/swtch.S ... %eip(戻り先) %ebp %ebx %esi %edi ... ... *new **old %eip(戻り先) %ebp %ebx %esi %edi %edx oldのカーネルスタック newのカーネルスタック *old %edx %esp %eax
  11. void swtch(struct context **old, struct context *new) • スタックから保存したレジスタを取り出して復元する https://github.com/mit-pdos/xv6-public/blob/master/swtch.S

    ... %eip(戻り先) ... ... *new **old %eip(戻り先) %ebp %ebx %esi %edi %edx oldのカーネルスタック newのカーネルスタック %eax %edx %esp
  12. void swtch(struct context **old, struct context *new) • リターンアドレス(関数の戻り先)にJMPして終了 https://github.com/mit-pdos/xv6-public/blob/master/swtch.S

    ... %eip(戻り先) ... ... *new **old %eip(戻り先) %ebp %ebx %esi %edi %edx oldのカーネルスタック newのカーネルスタック %eax %edx %esp
  13. 今日のコード • コード:コンテキストスイッチ ◦ struct context ◦ void swtch(struct context

    **old, struct context *new) • コード:スケジューリング ◦ void trap(struct trapframe *tf) ◦ void yield(void) ◦ void sched(void) ◦ void scheduler(void) • コード:mycpu & myproc ◦ struct cpu ◦ struct cpu* mycpu(void) ◦ struct proc* myproc(void)
  14. void yield(void) • ptable.lockを獲得する • proc->stateを更新する ◦ RUNNINGから ◦ RUNNABLEに

    • sched関数を呼び出す • ptable.lockを解放する https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L385
  15. void sched(void) • エラーチェックを行い, 不正な 状態だった場合, panic()を呼ぶ ◦ ptable.lockを獲得している ◦

    ロックされている ◦ プロセスが実行中である ◦ 割り込みを無効化している • 詳しくは4章 https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L365
  16. void sched(void) • 現在のCPUのintenaを保存する ◦ pushcliの前に割り込みが 有効だったかを表すフラグ ◦ 詳しくは4章 •

    intenaはカーネルスレッド固有 • スレッドが切り替わる際には保存し, 復帰した際に引き継ぐ必要がある https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L365
  17. void sched(void) • swtchを呼ぶ ◦ **old ▪ プロセスのcontext ◦ *new

    ▪ CPUのスケジューラ https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L365
  18. void scheduler(void) • CPUごとに実行され続ける スケジューラプロセス • 以下のループを繰り返す ◦ 実行するプロセスを選択 ◦

    コンテキストスイッチ ◦ 再びスケジューラに https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L32
  19. void scheduler(void) • 割り込みを有効にする ◦ 実行するプロセスが存在 しなかったときのため ◦ アイドリング •

    ptable.lockを獲得する • 何らかの処理 • ptable.lockを解放する https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L32
  20. void scheduler(void) • swtchを呼ぶ ◦ **old ▪ CPUのスケジューラ ◦ *new

    ▪ プロセスのcontext https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L32
  21. なぜptable.lockが必要なのか • xv6におけるinvariantの例 ◦ プロセス実行中にタイマー割り込みが発生した場合 , 別のプロセスにスイッチ可能 ▪ CPUのレジスタがプロセスの contextを保持している

    ▪ %cr3がプロセスのページテーブルを指している ▪ %espがプロセスのカーネルスタックを指している ◦ あるプロセスが実行可能のとき , スケジューラはそのプロセスを実行可能 ▪ プロセスのcontextがカーネルスレッドの値を保持している ▪ どのCPUもプロセスのカーネルスタック上で実行されていない ▪ どのCPUの%cr3もプロセスのページテーブルを指していない ▪ どのCPUのcpu->procもそのプロセスのことを指していない • invariantを保つために, ptable.lockの獲得が必要
  22. struct cpu* mycpu(void) • プロセスが実行されている CPUを返す • 割り込みを無効化してから 呼ばなくてはならない ◦

    関数実行中(for等)に 割り込みが発生 ◦ 違うCPUで実行される 可能性がある https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L37
  23. struct cpu* mycpu(void) • 割り込みが無効化されて いることを確認する ◦ // Interrupt Enable

    #define FL_IF 0x200 • 無効化されていなかったら panic()を呼ぶ https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L37
  24. struct proc* myproc(void) • 現在のCPUを取得し, 実行中のプロセスを返す • 割り込みを無効化し, 実行が 完了したら元に戻す

    ◦ 実行途中でプロセスが 切り替わる可能性 がある https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L57