Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
xv6 chapter5 first
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Tomoya Ishizaki
May 20, 2019
Programming
280
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
xv6 chapter5 first
Tomoya Ishizaki
May 20, 2019
More Decks by Tomoya Ishizaki
See All by Tomoya Ishizaki
Chompyらくとく便のこれまでとこれから
zaq1tomo
0
2.3k
【輪講】Ray: A Distributed Framework for Emerging AI Applications
zaq1tomo
2
310
xv6 chapter1 first
zaq1tomo
1
180
Hybrid Casper FFG
zaq1tomo
3
720
Other Decks in Programming
See All in Programming
JRuby: Past, Present, and Future
headius
0
220
プロダクトコードからライブラリの境界を見つける
elmetal
PRO
0
100
動作中のプログラムの中身をリアルタイムに覗く / Realtime Debugger for CSharp with Roslyn
prota
1
1.8k
Verilogで学ぶCPU自作入門.pdf
uyuki234
8
4k
Streamlitで実現する自然言語データアプリ開発
ayumu_yamaguchi
1
340
SREの越境 / SRE Collaboration
y0hgi
2
300
Turning Architecture into Unit Tests in the AI Era (NSSpain XIV)
steliosf
PRO
1
130
IBM Bob Dojo #1 仕様駆動開発入門
oniak3ibm
PRO
0
350
GitHubハンズオン講座 — 実務レベルのチーム開発のフローを身につけよう
junhat6
0
120
KiroのSpecで「五目並べ」を作ってみる
satoshi256kbyte
1
350
Everything will be SERVERLESS — 信じて運用した10年の経験値 / Everything Will be Serverless — Lessons Learned from 10 Years of Operational Experience
seike460
PRO
1
740
Security issues being discussed on Web Platforms
petamoriken
0
1.4k
Featured
See All Featured
Side Projects
sachag
456
43k
KATA
mclloyd
PRO
35
16k
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
920
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
490
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
340
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
203
76k
Building the Perfect Custom Keyboard
takai
2
890
Embracing the Ebb and Flow
colly
88
5.2k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
133
20k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
AI Search: Implications for SEO and How to Move Forward - #ShenzhenSEOConference
aleyda
1
1.4k
Designing for Timeless Needs
cassininazir
1
520
Transcript
xv6 chapter5 first Tomoya Ishizaki
はじめに • OSは複数のプロセスを一つの物理的なCPUに 多重化している • 多重化によって各プロセスがそれぞれ固有の 仮想的なCPUを持つように動作可能 • xv6がどのように多重化を実現しているか見ていく
今日の内容 スケジューリング(前半) • 多重化 • コンテキストスイッチ • スケジューリング
今日のコード • コード:コンテキストスイッチ ◦ 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)
今日の内容 スケジューリング(前半) • 多重化 • コンテキストスイッチ • スケジューリング
多重化
多重化とは • 一つの物理的な資源を複数に分割して使用し, 実際よりも多く見せること • 時間分割による多重化 ◦ CPUなど • 空間分割による多重化
◦ メモリなど
xv6はどのように多重化を実現しているか • 各CPUを時間分割し, プロセスを切り替える • xv6では2つのタイミングでプロセスを切り替え ◦ デバイスやパイプのI/Oなど(自分自身で待機) ▪ 詳しくは後半
◦ タイマー割り込みなど(強制的に切り替え) ▪ これから見ていく
多重化の実現には, いくつか課題が存在する 1. どうやって切り替えるか 2. どうやって透明的に行うか 3. どうやって競合を防ぐか 4. どうやって資源を解放するか
OSには, プロセス間調整の手段が必要 • 詳しくは後半 ◦ 親プロセスが子プロセスの終了を待つ ◦ パイプを読むとき, 書き込み完了を待つ ◦
デバイスI/O待ちなどCPUを使用していないとき, スリープして他プロセスを起こす
今日の内容 スケジューリング(前半) • 多重化 • コンテキストスイッチ • スケジューリング
コンテキストスイッチ
コンテキストスイッチとは • プロセスのコンテキストを切り替える • コンテキスト ◦ スタック ◦ レジスタ ◦
など
どうやってコンテキストスイッチを行うか • 低レイヤでは, 二種類の スイッチを行っている • 各CPUは, 固有の隔離された スケジューラスレッドをもつ •
ユーザープロセスが 他のユーザープロセスに 直接スイッチすることはない ユーザープロセスからスケジューラへ スケジューラからユーザープロセスへ
どうやってスレッドを切り替えるか • context ◦ xv6では5つのレジスタセットを保存する • 保存と復元 ◦ 現在のスレッドのcontextを保存 ◦
以前保存したスレッドのcontextを復元
今日のコード • コード:コンテキストスイッチ ◦ 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)
コード:コンテキストスイッチ
struct context • コンテキストスイッチのために 5つのレジスタセットを保存する https://github.com/mit-pdos/xv6-public/blob/master/proc.h#L27
struct context • 汎用レジスタの一部を保持する ◦ %edi, %esi, %ebx, %ebp •
%eipも保持する ◦ swtch()で明示的に操作している わけではないが, allocproc()内で 保存している ◦ 詳しくは1章 https://github.com/mit-pdos/xv6-public/blob/master/proc.h#L27
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
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
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
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
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
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
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
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
今日の内容 スケジューリング(前半) • 多重化 • コンテキストスイッチ • スケジューリング
スケジューリング
プロセスを切り替える一連の流れを見ていく • ユーザープロセスからスケジューラにスイッチ • スケジューラから別のユーザープロセスにスイッチ ※ sched関数がスケジューラへの入り口となる
スケジューラを呼ぶまでの一連の流れ • ptable.lockを獲得する • proc->stateを更新する • sched関数を呼び出す
今日のコード • コード:コンテキストスイッチ ◦ 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)
コード:スケジューリング
void trap(struct trapframe *tf) https://github.com/mit-pdos/xv6-public/blob/master/trap.c#L105 • プロセスが実行中かつタイマー割り込みが発生した場合, yield関数を呼び出す • 詳しくは3章
void yield(void) • ptable.lockを獲得する • proc->stateを更新する ◦ RUNNINGから ◦ RUNNABLEに
• sched関数を呼び出す • ptable.lockを解放する https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L385
void sched(void) • スケジューラへの入り口 • エラーチェックを行ってから スケジューラへスイッチする https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L365
void sched(void) • エラーチェックを行い, 不正な 状態だった場合, panic()を呼ぶ ◦ ptable.lockを獲得している ◦
ロックされている ◦ プロセスが実行中である ◦ 割り込みを無効化している • 詳しくは4章 https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L365
void sched(void) • 現在のCPUのintenaを保存する ◦ pushcliの前に割り込みが 有効だったかを表すフラグ ◦ 詳しくは4章 •
intenaはカーネルスレッド固有 • スレッドが切り替わる際には保存し, 復帰した際に引き継ぐ必要がある https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L365
void sched(void) • swtchを呼ぶ ◦ **old ▪ プロセスのcontext ◦ *new
▪ CPUのスケジューラ https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L365
void scheduler(void) • CPUごとに実行され続ける スケジューラプロセス • 以下のループを繰り返す ◦ 実行するプロセスを選択 ◦
コンテキストスイッチ ◦ 再びスケジューラに https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L32
void scheduler(void) • 割り込みを有効にする ◦ 実行するプロセスが存在 しなかったときのため ◦ アイドリング •
ptable.lockを獲得する • 何らかの処理 • ptable.lockを解放する https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L32
void scheduler(void) • 実行可能なプロセスが見つかる まで順番に探索する • ラウンドロビン https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L32
void scheduler(void) • CPUのプロセスに選択した プロセスを設定する • switchuvm()でプロセスの ページテーブルに切り替える ◦ 詳しくは2章
• プロセスを実行中にする https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L32
void scheduler(void) • swtchを呼ぶ ◦ **old ▪ CPUのスケジューラ ◦ *new
▪ プロセスのcontext https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L32
void scheduler(void) • ページテーブルを切り替える ◦ 詳しくは2章 • CPUのプロセスを初期化する https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L32
なぜptable.lockが必要なのか • xv6におけるinvariantの例 ◦ プロセス実行中にタイマー割り込みが発生した場合 , 別のプロセスにスイッチ可能 ▪ CPUのレジスタがプロセスの contextを保持している
▪ %cr3がプロセスのページテーブルを指している ▪ %espがプロセスのカーネルスタックを指している ◦ あるプロセスが実行可能のとき , スケジューラはそのプロセスを実行可能 ▪ プロセスのcontextがカーネルスレッドの値を保持している ▪ どのCPUもプロセスのカーネルスタック上で実行されていない ▪ どのCPUの%cr3もプロセスのページテーブルを指していない ▪ どのCPUのcpu->procもそのプロセスのことを指していない • invariantを保つために, ptable.lockの獲得が必要
コード:mycpu & myproc
struct cpu • それぞれのCPUの状態を管理する ◦ Local APIC(プロセッサ固有のID) ◦ スケジューラ ◦
など https://github.com/mit-pdos/xv6-public/blob/master/proc.h#L2
struct cpu* mycpu(void) • プロセスが実行されている CPUを返す • 割り込みを無効化してから 呼ばなくてはならない ◦
関数実行中(for等)に 割り込みが発生 ◦ 違うCPUで実行される 可能性がある https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L37
struct cpu* mycpu(void) • 割り込みが無効化されて いることを確認する ◦ // Interrupt Enable
#define FL_IF 0x200 • 無効化されていなかったら panic()を呼ぶ https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L37
struct cpu* mycpu(void) • Local APICを取り出す ◦ 詳しくは3章 https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L37
https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L37 struct cpu* mycpu(void) • CPUはグローバルな配列 cpus[]で管理 • Local APICが一致する
CPUが見つかったら返す • 見つからなかった場合 panic()を呼ぶ
struct proc* myproc(void) • 現在のCPUを取得し, 実行中のプロセスを返す • 割り込みを無効化し, 実行が 完了したら元に戻す
◦ 実行途中でプロセスが 切り替わる可能性 がある https://github.com/mit-pdos/xv6-public/blob/master/proc.c#L57
ご清聴ありがとうございました