45
【幕張読書会】 UNIXカーネルの設計 3バッファキャッシュ 担当: @ktateish

【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

Embed Size (px)

Citation preview

Page 1: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

【幕張読書会】

UNIXカーネルの設計

3章 バッファキャッシュ担当: @ktateish

Page 2: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

前回の復習(1) カーネルアーキテクチャ

ユーザプログラム→システムコール→ファイルシステム→バッファキャッシュ (今日はココ)→ブロックデバイス→デバイスドライバ→ディスクコントローラ

→物理ディスク

Page 3: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

前回の復習(2) プロセスの状態遷移

● 実行中(ユーザモード)● 実行中(カーネルモード)● 休眠状態● 実行可能状態

本書のカーネルはカーネルモード実行中はコンテキストスイッチしない。割り込みはかかるが必要に応じて禁止することも。いずれもカーネル空間のデータ破壊を防ぐため

Page 4: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

バッファキャッシュ概要

● ブロックデバイスの内容をメモリに保持● データをブロック単位で管理● ヘッダ部とデータ部。ヘッダ重要● ハッシュと双方向リンクリストで管理● LRU(Least Recently Used)

Page 5: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

意識しておくこと

● ディスクに対してメモリは圧倒的に高速● 非同期書込してよければそうしたい(高効率)● ディスクとバッファキャッシュの整合性● ディスクへの非同期書き込み完了は割り込みコ

ンテキストで処理される(随所で割り込まれ、バッファが操作される)

Page 6: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

バッファヘッダ

● デバイス番号(簡単のため以後省略)● ブロック番号● データ部へのポインタ● バッファ状態変数(ロック中、要遅延書込等)● ハッシュ用リストポインタ*2(prev,next)● 自由リスト用ポインタ*2

Page 7: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

バッファキャッシュ概観

● ブロック番号をキーとしたハッシュ● ひとつの自由リスト

m1

m6

m0

FL

m2

15

6

7

44

92

41

14

2

29

13

35

58

20

42

72

… … … …16ハッシュ

自由リスト

Page 8: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

ハッシュ(1)● デバイス番号、ブロック番号をキーとした高速な

アクセス用● ブロック番号 mod ハッシュサイズ でリストヘッド

(番兵)を特定

m1

m6

m0

m2

15

6

7

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16ブロック92にアクセスしたい場合は 92 mod 7 = 1 なので、このリストを探す

Page 9: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

ハッシュ(2)● リスト内は線形検索。順序なし● ふたつ以上のバッファが同じブロック内容を保

持することはできない(整合性のため)● (補足)リストの最後は番兵にループ

m1

m6

m0

m2

15

6

7

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16このリストをたどって (線形検索して)92に到達。存在しなければキャッシュミス。

Page 10: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

自由リスト(1)● {再,新規}割当て可能なバッファのリスト● System起動時、全バッファはここに繋がる● 一度有効なデータ(ブロック)を保持するとハッ

シュにもつながれる(下図)

m1

m6

m0

FL

m2

15

6

7

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16

Page 11: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

自由リスト(2)● バッファを使用中(プロセス/ディスクとの読み書

き)は自由リストから外される● バッファの使用がおわったら自由リストの最後

尾に繋がれる

m1

m6

m0

FL

m2

15

6

7

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16

使用が終わった=再利用可能になったバッファは最後尾に追加

Page 12: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

自由リスト(3)● ハッシュを検索してヒットした場合は自由リスト

の途中からとりだされる● {新規,再}割り当ての際、先頭から取り出す● つまり古いバッファから再利用 → LRU!

m1

m6

m0

FL

m2

15

6

7

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16

キャッシュヒットの場合は途中からはずす

キャッシュミスした場合は自由リストの先頭を取り出して目的のブロックに再割当て

Page 13: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

つまりどういうことだってばよ?

バッファキャッシュはLRU(最後に参照されてから最も時間がたったバッファを入れ替える)をハッシュとリストの組み合わせで実現している。

引き続き具体的なインターフェースについて解説する。

が、その前に・・・

Page 14: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

注意事項(ロックについて)

● バッファを「ロックする」、という表現が多用されるが、これは「上位または下位レイヤで使用中のフラグをたてる」という意味。

● 「使用中」とは、バッファがユーザ空間(上位)との間あるいはディスク(下位)との間でコピー中であるということ。

Page 15: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

注意事項(ロックについて)

● バッファは上下レイヤでのコピー中のみロックされることに注意が必要。

● たとえばユーザ空間にコピーされたデータはコピー元となったバッファのロックとは無関係に操作される。

Page 16: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

インターフェース

● getblk バッファを割り当てて取得(←重要)● brelse バッファを自由リストに戻す● bread ブロック読み込み● breada ブロック先読み● bwrite ブロック書き込み

Page 17: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

getblk (1)

● ブロックをキーとして呼び出される● 戻り値は当該ブロックに対応するバッファ● まずハッシュを検索● ロックされてなければそれをreturn● されてる場合は解除を待ってやり直し● ハッシュ内に見つからなければ、自由リストから

の取り出しへ

Page 18: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

getblk (2)

● 自由リストの先頭を取り出す● 遅延書き込みフラグが立ってなければそれを

return● 立っていたら書き込み依頼してやりなおし

それでは擬似コードをどうぞ

Page 19: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

Case1: ハッシュから取り出し

● ハッシュ内にバッファを見つけ、ロックされてなかったケース

m1

m6

m0

FL

m2

15

6

7

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16

ブロック番号72を取り出すとする。まず 72 mod 7 ≡ 2 なので m2 リストを探す

ブロック72のバッファが見つかった!

Page 20: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

Case1: ハッシュから取り出し

● バッファをロックして● 自由リストから取り除いて● 該当バッファをreturn

m1

m6

m0

FL

m2

15

6

7

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16

ロック(状態変数に使用中のフラグをたてる)して

自由リストから取り除く

Page 21: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

Case2:自由リストから再利用

● ハッシュ内にバッファが見つからず、自由リストから再利用するケース

m1

m6

m0

FL

m2

15

6

7

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16

ブロック番号8を取り出すとする。まず 8 mod 7 ≡ 1 なので m1 リストを探す

リストを一周してもブロック8のバッファは見つからなかった

Page 22: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

Case2:自由リストから再利用

● ハッシュに見つからないので、自由リストの先頭を取り出して再利用する

m1

m6

m0

FL

m2

15

6

7

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16

自由リストの先頭を取り出す

Page 23: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

Case2:自由リストから再利用

● ハッシュに見つからないので、自由リストの先頭を取り出して再利用する

m1

m6

m0

FL

m2

15

6

7

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16

自由リストの先頭を取り出す

Page 24: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

Case2:自由リストから再利用

● 遅延書き込みフラグが立っていないことを確認(立ってたらCase3)

● ハッシュから取り除く

m1

m6

m0

FL

m2

15

8

7

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16

遅延書き込みフラグなし

6 → 8用のバッファにする

Page 25: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

Case2:自由リストから再利用

● ハッシュの新しい位置に挿入● バッファをreturn

m1

m6

m0

FL

m2

15 8

7

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16

8は m1 リストにあるべきなので、ここにつなぐ

Page 26: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

Case3:遅延書き込み

● 自由リストから再利用しようとしたが、遅延書き込みフラグが立っていたケース

● ハッシュ内にないので自由リストの先頭を取り出す

m1

m6

m0

FL

m2

15

6

7

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16

ブロック番号8を取り出すとする。自由リストの先頭を取り出すところまでは一緒

Page 27: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

Case3:遅延書き込み

● とりだした6のバッファには遅延書き込みフラグが立っていた=再利用できない

● ディスクへの書き込みを依頼してハッシュを探すところからやり直し

m1

m6

m0

FL

m2

15

6

7

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16

遅延書き込みフラグON=再利用不可。ディスクへ書き込み依頼

Page 28: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

Case3:遅延書き込み

● 再び自由リストの先頭から取り出したが、これも遅延書き込みフラグが立っていた

● 見つかるまで繰り返す

m1

m6

m0

FL

m2

15

6

7

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16

またハッシュにないので自由リスト先頭から取り出したがまた遅延書き込みフラグ

書き込み中

次はこいつ

Page 29: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

Case3:遅延書き込み

● 遅延書き込みフラグがないバッファを見つけたら、Case2で見たように再利用

m1

m6

m0

FL

m2

15

6

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16

ハッシュの位置更新など

書き込み中

書き込み中

8

Page 30: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

Case3:遅延書き込み

● 書き込みが終わったバッファは自由リストの先頭に戻される(書き込み直後だが、必要とされたのはだいぶ前なので)

m1

m6

m0

FL

m2

15

6

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16

8

自由リストの先頭に戻す

Page 31: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

Case3:遅延書き込み(ポイント)● ディスクの書き込みは遅いので完了を同期的に

待つより別のバッファを探したほうが速い● それに、スリープしてしまうと目覚めたときに別

のプロセスコンテキストによりバッファがロックされている可能性があり、書き込まれたバッファをそのまま使えない

● → 結局while文をやりなおすしかない

Page 32: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

Case4:自由リストが空

● 自由リストから再利用しようとしたが、自由リストにバッファがないケース

● この場合は自由リストにバッファが追加されるまでスリープする

m1

m6

m0

FL

m2

15

6

7

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16

ブロック番号8を取り出すとする。自由リストの先頭を取り出そうとするが、空だった。

Page 33: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

Case4:自由リストが空

● このケースも目覚めたらwhile文やりなおし● このプロセスコンテキストが目覚める前に、一緒

に目覚めた別のコンテキストが実行可能状態になり、バッファを同じブロックに割り当てる可能性に対応するため

● 言い換えると、目覚めた後ハッシュを確認せずに自由リストを使うと二重割り当てしてしまうから

Page 34: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

Case5: ハッシュから取り出し

● ハッシュ内にバッファを見つけたが、ロックされていたケース

m1

m6

m0

FL

m2

15

6

7

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16

ブロック番号72を取り出すとする。

ブロック72のバッファが見つかった!でもロックされてる

Page 35: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

Case5: ハッシュから取り出し

● 要求ありのフラグをバッファに立てて、アンロックを待ってスリープ

● 目覚めた後はwhile文をやり直し● 理由はCase3,4と同様

m1

m6

m0

FL

m2

15

6

7

44

92

41

14

2

29

13

35

58

20

42

72

… … … …

16

要求あり

Page 36: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

インターフェース

● getblk バッファを割り当てて取得● brelse バッファを自由リストに戻す● bread ブロック読み込み● breada ブロック先読み● bwrite ブロック書き込み

Page 37: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

brelse (1)

● ロックされたバッファを引数に呼び出される● 戻り値はなし● 自由リストのバッファ追加まちのプロセスを起こ

す● 引数バッファのアンロックまちのプロセスを起こ

Page 38: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

brelse (2)

● 割り込みを禁止して● 有効なデータが入っていて、古くないバッファは

自由リストの最後に● そうでない(IOエラーや遅延書き込み後)バッファ

は自由リストの先頭につなぐ● 割り込み禁止を解除してバッファをアンロック

Page 39: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

インターフェース

● getblk バッファを割り当てて取得● brelse バッファを自由リストに戻す● bread ブロック読み込み● breada ブロック先読み● bwrite ブロック書き込み

Page 40: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

bread

● ブロック番号を引数にして、データを読み込んだバッファを返す

● バッファの取得にはgetblk()を使用● バッファに有効なデータが乗っていればそのま

まもどし、乗ってなければディスクから読み込んで戻す

● シーケンシャルに読み込む場合は次のブロックをバッファに載せたい→breada

Page 41: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

インターフェース

● getblk バッファを割り当てて取得● brelse バッファを自由リストに戻す● bread ブロック読み込み● breada ブロック先読み● bwrite ブロック書き込み

Page 42: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

breada

● ブロック番号を引数にして、データを読み込んだバッファを返す(breadと同じ)

● ただし、引数のブロック番号の次のブロックにも非同期で読み込みをディスクに依頼する

● 詳細は擬似コードで

Page 43: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

インターフェース

● getblk バッファを割り当てて取得● brelse バッファを自由リストに戻す● bread ブロック読み込み● breada ブロック先読み● bwrite ブロック書き込み

Page 44: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

bwrite

● バッファを引数にして、そのバッファのデータをディスクに書き込む

● 同期書き込みの場合はバッファのアンロックbrelse()まで実施

● 遅延書き込みの場合は「内容が古いフラグ」をたてて、割り込みコンテキストからbrelse()でアンロックされた場合に自由リストの先頭に追加されるようにする

Page 45: 【幕張読書会】Unixカーネルの設計 3(バッファキャッシュ)

まとめ&感想

● バッファキャッシュは最近アクセスしたものをキャッシュに載せておく(LRU)

● 自由リストの順番が「長く使ってない順」そのもの

● ハッシュと二重リンクリストの性質をうまく使っている

● コンテキストスイッチと割り込み怖い