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
Haskell/Servantを通してWebミドルウェアを捉え直す
pizzacat83
0
430
Honoでのサプライチェーン侵害対策 〜 3つのライブラリに学ぶ
yusukebe
7
1.8k
これからAgentCoreを触る方へトレンドはGatewayです
har1101
6
480
なぜ関数型プログラミングで「型」と「証明」が語られるのか #fp_matsuri
kajitack
3
650
Mujeres en SEO Summit 2026 - Greatest Disaster Hits en Web Performance
guaca
0
240
Semantic Version 単位で戦略を柔軟に変えて、パッケージアップデートを自動化する
daitasu
1
340
TypeScript+Orvalで実現する型安全かつ堅牢でスケーラブルなマルチチャネル通知基盤 / TSKaigi Night talks ~after conference~
d0riven
0
390
LLMによるContent Moderationの本番運用の裏側と品質担保への挑戦
suikabar
3
830
分散システム、なんですぐ死んでしまうん?耐障害性を高めたいあなたのためのレジリエンスパターン入門
mshibuya
7
4.7k
コンテキストの使い捨てをやめる — ビジネスルール駆動開発と miko —
ioki
0
270
TAKTでAI駆動開発の品質を設計する
j5ik2o
7
1.7k
Creating Composable Callables in Contemporary C++
rollbear
0
190
Featured
See All Featured
Impact Scores and Hybrid Strategies: The future of link building
tamaranovitovic
0
330
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.6k
GraphQLの誤解/rethinking-graphql
sonatard
75
12k
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
350
Noah Learner - AI + Me: how we built a GSC Bulk Export data pipeline
techseoconnect
PRO
0
210
Optimizing for Happiness
mojombo
378
71k
Leo the Paperboy
mayatellez
8
1.9k
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
310
A Modern Web Designer's Workflow
chriscoyier
698
190k
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
320
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
220
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
5
1.1k
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
ご清聴ありがとうございました