Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
xv6 chapter5 first
Search
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.2k
【輪講】Ray: A Distributed Framework for Emerging AI Applications
zaq1tomo
2
300
xv6 chapter1 first
zaq1tomo
1
170
Hybrid Casper FFG
zaq1tomo
3
710
Other Decks in Programming
See All in Programming
The NotImplementedError Problem in Ruby
koic
1
920
The ROI of Quarkus for Spring Boot Applications
hollycummins
0
140
さぁV100、メモリをお食べ・・・
nilpe
0
150
セキュリティの専門家じゃなくてもできる。「セキュリティ意識」をアップデートして サプライチェーン攻撃への耐性を高めよう。
tk3fftk
5
920
なぜ型を書くのか? TSKaigi2026で改めて考える #tskaigi_smarthr
kajitack
0
140
鹿野さんに聞く!『TypeScriptコードレシピ集』で磨く実践力
tonkotsuboy_com
2
730
JavaDoc 再入門
nagise
1
410
Observability in Practice:Grafana 與 Edge Device SRE 的那些事
blueswen
0
170
ADKを使って簡単にAIエージェントを作ってみよう
k1mu21
0
280
Signal Forms: Details & Live Coding @enterJS 2026 in Mannheim
manfredsteyer
PRO
0
190
「AIで開発し、AIを届ける」をEvalでつなぐ 〜AIネイティブに始めるプロダクト開発の実践〜 / Connecting "Develop with AI, deliver AI" with Eval
rkaga
4
5.4k
Spring Security 実践 ─ GraphQL APIで実務に役立つ 認証・認可 を学ぶ
wagyu
0
260
Featured
See All Featured
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6k
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
370
Tell your own story through comics
letsgokoyo
1
960
Designing for Performance
lara
611
70k
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
180
How People are Using Generative and Agentic AI to Supercharge Their Products, Projects, Services and Value Streams Today
helenjbeal
1
220
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
2.8k
Technical Leadership for Architectural Decision Making
baasie
3
420
From π to Pie charts
rasagy
0
220
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
1.1k
Automating Front-end Workflow
addyosmani
1370
210k
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
140
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
ご清聴ありがとうございました