xv6のコンテキストスイッチを読む

31
xv6のコンテキストスイッチを読む @mfumi2

description

第2回APASEC+第2期サイボウズ・ラボユース合同勉強会 (2013/3/29) 発表資料

Transcript of xv6のコンテキストスイッチを読む

Page 1: xv6のコンテキストスイッチを読む

xv6のコンテキストスイッチを読む

@mfumi2

Page 2: xv6のコンテキストスイッチを読む

自己紹介

● セキュリティキャンプ2012OS組

Page 3: xv6のコンテキストスイッチを読む

最近こんな本が出ました

はじめてのOSコードリーディング青柳隆宏著

おすすめ! でも...

Page 4: xv6のコンテキストスイッチを読む

UNIX V6 を読む上で大変なところ

● PDP11● Cの記法が古い (pre K&R)● アセンブラの記法が特殊● デフォルトが8進数● etc...

Page 5: xv6のコンテキストスイッチを読む

xv6とは

● MITで開発された教育用OS http://pdos.csail.mit.edu/6.828/2012/xv6.html

● UNIX V6 を x86 向けに書き直したもの● 解説書有り

http://pdos.csail.mit.edu/6.828/2012/xv6/book-rev7.pdf

Page 6: xv6のコンテキストスイッチを読む

xv6の特徴

● x86● マルチプロセッサ対応● バイナリはELF● スワッピング機能はなし● qemu+gdb で動作可

Page 7: xv6のコンテキストスイッチを読む

コンテキストスイッチ

● プロセスのコンテキストを切り替える● コンテキスト …

– メモリ空間→ cr3 レジスタを切り替える

– スタック→ %esp を切り替える

– 各種レジスタ→ プロセスごとのスタックに退避&スタックから復帰

Page 8: xv6のコンテキストスイッチを読む

xv6のコンテキストスイッチ• switch.S

void swtch(struct context **old, struct context *new);

• proc.h

Page 9: xv6のコンテキストスイッチを読む

xv6のコンテキストスイッチ• switch.S

void swtch(struct context **old, struct context *new);

%edi%esi%ebx%ebp%eip(swtchの戻り先)

メモリ%%%%%eip(swtchの戻り先)

%esp★

new(%edx)

Page 10: xv6のコンテキストスイッチを読む

xv6のコンテキストスイッチ• switch.S

void swtch(struct context **old, struct context *new);

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%esp

メモリ

new(%edx)

Page 11: xv6のコンテキストスイッチを読む

xv6のコンテキストスイッチ• switch.S

void swtch(struct context **old, struct context *new);

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%esp

メモリ

new(%edx)

Page 12: xv6のコンテキストスイッチを読む

xv6のコンテキストスイッチ• switch.S

void swtch(struct context **old, struct context *new);

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%espメモリ

new(%edx)

Page 13: xv6のコンテキストスイッチを読む

xv6のコンテキストスイッチ• switch.S

void swtch(struct context **old, struct context *new);

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%espメモリ

new(%edx)

Page 14: xv6のコンテキストスイッチを読む

xv6のコンテキストスイッチ• switch.S

void swtch(struct context **old, struct context *new);

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%esp

*old(%eax)

メモリ

★ new(%edx)

Page 15: xv6のコンテキストスイッチを読む

xv6のコンテキストスイッチ• switch.S

void swtch(struct context **old, struct context *new);

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%esp

メモリ

*old(%eax)

new(%edx)

Page 16: xv6のコンテキストスイッチを読む

xv6のコンテキストスイッチ• switch.S

void swtch(struct context **old, struct context *new);

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%esp

メモリ

new(%edx)

*old(%eax)

Page 17: xv6のコンテキストスイッチを読む

xv6のコンテキストスイッチ• switch.S

void swtch(struct context **old, struct context *new);

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%esp

メモリ

new(%edx)

*old(%eax)

Page 18: xv6のコンテキストスイッチを読む

xv6のコンテキストスイッチ• switch.S

void swtch(struct context **old, struct context *new);

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%esp

メモリ

new(%edx)

*old(%eax)

Page 19: xv6のコンテキストスイッチを読む

xv6のコンテキストスイッチ• switch.S

void swtch(struct context **old, struct context *new);

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%edi%esi%ebx%ebp%eip(swtchの戻り先)

%esp

メモリ

new(%edx)

*old(%eax)

Page 20: xv6のコンテキストスイッチを読む

xv6のコンテキストスイッチの流れ

ユーザプロセス1 ユーザプロセス2 タイマ割り込み iret

  割り込みハンドラ スケジューラ 割り込みハンドラ swtch() swtch()

Page 21: xv6のコンテキストスイッチを読む

(1) 割り込みの発生

● xv6では100msごとにタイマ割り込みが発生● 割り込みが発生すると

– 特権レベルが3から0に移行– スタックがカーネルスタックに切り替わる

(TSSからカーネル用%espと%ssを読み込む)– スタックに%ss, %esp, %eflags, %cs, %eip を積む

(現在実行していたプロセスの状態を積む)– 対応する割り込みハンドラに移行

Page 22: xv6のコンテキストスイッチを読む

(1) 割り込みの発生

xv6では● vectors.S (vectors.plから生成される) 内に定義してあ

る vector$i() ($iは割り込み番号) が呼び出される(割り込みハンドラの設定は trap.c の tvinit(), idtinit(), いずれも main.cのmain()から呼ばれる )

● vector$i から trapasm.S の alltraps() に飛ぶ

Page 23: xv6のコンテキストスイッチを読む

(2) 割り込みハンドラの処理● trapasm.S alltraps

・自動に退避されないレジスタを退避・カーネル用セグメントの設定・最終的に trap.c の trap()を呼ぶ

Page 24: xv6のコンテキストスイッチを読む

(2) 割り込みハンドラの処理

● trap.c の trap() でそれぞれの処理に応じた割り込み処理をおこなう(割り込みハンドラごとに処理をおこなっていない)

● タイマ割り込みの場合 CPU 時間切れなら proc.c の yield()を呼ぶ● yield()からさらに proc.cの shced()が呼ばれる

タイマ割り込み → vectors.S#vector$i() → trapasm.S#alltraps() → trap.c#trap() → proc.c#yield() → proc.c#shced()

Page 25: xv6のコンテキストスイッチを読む

(2) 割り込みハンドラの処理● proc.c の sched()

スケジューラプロセスにコンテキストスイッチする

・proc‥現在のプロセスの情報をもつ構造体・cpu‥各CPUが持つCPUの情報をもつ構造体それぞれproc.h で定義

コンテキストの切り替え•swtch()からの戻り先 (cpu->scheduler.eip) は proc.c の scheduler()

Page 26: xv6のコンテキストスイッチを読む

(3) スケジューラの処理

★ sched()のswtch()からの戻り先メモリ空間をカーネル用に切り替え

次に実行するプロセスを探す(ラウンドロビン)

メモリ空間の切り替え選択したプロセスにswtch()

proc.c scheduler()

Page 27: xv6のコンテキストスイッチを読む

(3) スケジューラの処理

● 何故sched()のswtch()からscheduler()に戻るのか?→ ブートローダから呼ばれることになる main.c の main()でinitプロセス(proc[0])を起動後shceduler()を呼ぶため

Page 28: xv6のコンテキストスイッチを読む

(4) 割り込みハンドラの処理(後半)● proc.c の sched()

scheduler() から swtch() によって選択されたプロセスの再開

ここから処理が再開★

Page 29: xv6のコンテキストスイッチを読む

(4) 割り込みハンドラの処理(後半)

● 最終的にalltrap() まで return し,そこで iret して割り込み終了● iret でスタックからプロセスの状態を復帰させる.このとき特権レ

ベルも3に戻るスタックが切り替わっているので別のプロセス状態に戻る

trapasm.S

%eip%cs%eflags%esp%ss

メモリ

Page 30: xv6のコンテキストスイッチを読む

xv6のコンテキストスイッチの流れ(再掲)

ユーザプロセス1 ユーザプロセス2 タイマ割り込み iret

  割り込みハンドラ スケジューラ 割り込みハンドラ swtch() swtch()

Page 31: xv6のコンテキストスイッチを読む

まとめ

● PDP11がいやな人はxv6読んでみたらどうでしょうか● いろいろと改造のしどころ有り