狠狠撸

狠狠撸Share a Scribd company logo
【幕張読書会】
UNIXカーネルの設計
3章 バッファキャッシュ
担当: @ktateish
前回の復習(1) カーネルアーキテクチャ
ユーザプログラム
→システムコール
→ファイルシステム
→バッファキャッシュ (今日はココ)
→ブロックデバイス
→デバイスドライバ
→ディスクコントローラ
→物理ディスク
前回の復習(2) プロセスの状態遷移
● 実行中(ユーザモード)
● 実行中(カーネルモード)
● 休眠状態
● 実行可能状態
本書のカーネルはカーネルモード実行中はコンテ
キストスイッチしない。割り込みはかかるが必要に
応じて禁止することも。いずれもカーネル空間の
データ破壊を防ぐため
バッファキャッシュ概要
● ブロックデバイスの内容をメモリに保持
● データをブロック単位で管理
● ヘッダ部とデータ部。ヘッダ重要
● ハッシュと双方向リンクリストで管理
● LRU(Least Recently Used)
意識しておくこと
● ディスクに対してメモリは圧倒的に高速
● 非同期書込してよければそうしたい(高効率)
● ディスクとバッファキャッシュの整合性
● ディスクへの非同期書き込み完了は割り込みコ
ンテキストで処理される(随所で割り込まれ、
バッファが操作される)
バッファヘッダ
● デバイス番号(簡単のため以後省略)
● ブロック番号
● データ部へのポインタ
● バッファ状態変数(ロック中、要遅延書込等)
● ハッシュ用リストポインタ*2(prev,next)
● 自由リスト用ポインタ*2
バッファキャッシュ概観
● ブロック番号をキーとしたハッシュ
● ひとつの自由リスト
m1
m6
m0
FL
m2
15
6
7
44
92
41
14
2
29
13
35
58
20
42
72
…
…
…
…
16ハッシュ
自由リスト
ハッシュ(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 な
ので、このリストを探す
ハッシュ(2)
● リスト内は線形検索。順序なし
● ふたつ以上のバッファが同じブロック内容を保
持することはできない(整合性のため)
● (補足)リストの最後は番兵にループ
m1
m6
m0
m2
15
6
7
44
92
41
14
2
29
13
35
58
20
42
72
…
…
…
…
16
このリストをたどって (線形
検索して)92に到達。存在
しなければキャッシュミス。
自由リスト(1)
● {再,新規}割当て可能なバッファのリスト
● System起動時、全バッファはここに繋がる
● 一度有効なデータ(ブロック)を保持するとハッ
シュにもつながれる(下図)
m1
m6
m0
FL
m2
15
6
7
44
92
41
14
2
29
13
35
58
20
42
72
…
…
…
…
16
自由リスト(2)
● バッファを使用中(プロセス/ディスクとの読み書
き)は自由リストから外される
● バッファの使用がおわったら自由リストの最後
尾に繋がれる
m1
m6
m0
FL
m2
15
6
7
44
92
41
14
2
29
13
35
58
20
42
72
…
…
…
…
16
使用が終わった=再利用
可能になったバッファは最
後尾に追加
自由リスト(3)
● ハッシュを検索してヒットした場合は自由リスト
の途中からとりだされる
● {新規,再}割り当ての際、先頭から取り出す
● つまり古いバッファから再利用 → LRU!
m1
m6
m0
FL
m2
15
6
7
44
92
41
14
2
29
13
35
58
20
42
72
…
…
…
…
16
キャッシュヒットの場合は
途中からはずす
キャッシュミスした場合は自由
リストの先頭を取り出して目的
のブロックに再割当て
つまりどういうことだってばよ?
バッファキャッシュはLRU(最後に参照されてから
最も時間がたったバッファを入れ替える)をハッシュ
とリストの組み合わせで実現している。
引き続き具体的なインターフェースについて解説す
る。
が、その前に???
注意事項(ロックについて)
● バッファを「ロックする」、という表現が多用され
るが、これは「上位または下位レイヤで使用中
のフラグをたてる」という意味。
● 「使用中」とは、バッファがユーザ空間(上位)と
の間あるいはディスク(下位)との間でコピー中で
あるということ。
注意事項(ロックについて)
● バッファは上下レイヤでのコピー中のみロックさ
れることに注意が必要。
● たとえばユーザ空間にコピーされたデータはコ
ピー元となったバッファのロックとは無関係に操
作される。
インターフェース
● getblk バッファを割り当てて取得(←重要)
● brelse バッファを自由リストに戻す
● bread ブロック読み込み
● breada ブロック先読み
● bwrite ブロック書き込み
getblk (1)
● ブロックをキーとして呼び出される
● 戻り値は当該ブロックに対応するバッファ
● まずハッシュを検索
● ロックされてなければそれをreturn
● されてる場合は解除を待ってやり直し
● ハッシュ内に見つからなければ、自由リストから
の取り出しへ
getblk (2)
● 自由リストの先頭を取り出す
● 遅延書き込みフラグが立ってなければそれを
return
● 立っていたら書き込み依頼してやりなおし
それでは擬似コードをどうぞ
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のバッ
ファが見つかっ
た!
Case1: ハッシュから取り出し
● バッファをロックして
● 自由リストから取り除いて
● 該当バッファをreturn
m1
m6
m0
FL
m2
15
6
7
44
92
41
14
2
29
13
35
58
20
42
72
…
…
…
…
16
ロック(状態変数に使用中のフ
ラグをたてる)して
自由リストから取り除く
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の
バッファは見つ
からなかった
Case2:自由リストから再利用
● ハッシュに見つからないので、自由リストの先頭
を取り出して再利用する
m1
m6
m0
FL
m2
15
6
7
44
92
41
14
2
29
13
35
58
20
42
72
…
…
…
…
16
自由リストの先頭を取り出す
Case2:自由リストから再利用
● ハッシュに見つからないので、自由リストの先頭
を取り出して再利用する
m1
m6
m0
FL
m2
15
6
7
44
92
41
14
2
29
13
35
58
20
42
72
…
…
…
…
16
自由リストの先頭を取り出す
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用のバッファにする
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 リストにあるべきなの
で、ここにつなぐ
Case3:遅延書き込み
● 自由リストから再利用しようとしたが、遅延書き
込みフラグが立っていたケース
● ハッシュ内にないので自由リストの先頭を取り
出す
m1
m6
m0
FL
m2
15
6
7
44
92
41
14
2
29
13
35
58
20
42
72
…
…
…
…
16
ブロック番号8を取り出すとす
る。
自由リストの先頭を取り出す
ところまでは一緒
Case3:遅延書き込み
● とりだした6のバッファには遅延書き込みフラグ
が立っていた=再利用できない
● ディスクへの書き込みを依頼してハッシュを探す
ところからやり直し
m1
m6
m0
FL
m2
15
6
7
44
92
41
14
2
29
13
35
58
20
42
72
…
…
…
…
16
遅延書き込みフラグ ON=再利
用不可。
ディスクへ書き込み依頼
Case3:遅延書き込み
● 再び自由リストの先頭から取り出したが、これも
遅延書き込みフラグが立っていた
● 見つかるまで繰り返す
m1
m6
m0
FL
m2
15
6
7
44
92
41
14
2
29
13
35
58
20
42
72
…
…
…
…
16
またハッシュにないので自由リ
スト先頭から取り出したが
また遅延書き込みフラグ
書き込み中
次はこいつ
Case3:遅延書き込み
● 遅延書き込みフラグがないバッファを見つけた
ら、Case2で見たように再利用
m1
m6
m0
FL
m2
15
6
44
92
41
14
2
29
13
35
58
20
42
72
…
…
…
…
16
ハッシュの位置更新など
書き込み中
書き込み中
8
Case3:遅延書き込み
● 書き込みが終わったバッファは自由リストの先
頭に戻される(書き込み直後だが、必要とされた
のはだいぶ前なので)
m1
m6
m0
FL
m2
15
6
44
92
41
14
2
29
13
35
58
20
42
72
…
…
…
…
16
8
自由リストの先頭に戻す
Case3:遅延書き込み(ポイント)
● ディスクの書き込みは遅いので完了を同期的に
待つより別のバッファを探したほうが速い
● それに、スリープしてしまうと目覚めたときに別
のプロセスコンテキストによりバッファがロックさ
れている可能性があり、書き込まれたバッファを
そのまま使えない
● → 結局while文をやりなおすしかない
Case4:自由リストが空
● 自由リストから再利用しようとしたが、自由リスト
にバッファがないケース
● この場合は自由リストにバッファが追加されるま
でスリープする
m1
m6
m0
FL
m2
15
6
7
44
92
41
14
2
29
13
35
58
20
42
72
…
…
…
…
16
ブロック番号8を取り出すとす
る。
自由リストの先頭を取り出そう
とするが、空だった。
Case4:自由リストが空
● このケースも目覚めたらwhile文やりなおし
● このプロセスコンテキストが目覚める前に、一緒
に目覚めた別のコンテキストが実行可能状態に
なり、バッファを同じブロックに割り当てる可能
性に対応するため
● 言い換えると、目覚めた後ハッシュを確認せず
に自由リストを使うと二重割り当てしてしまうか
ら
Case5: ハッシュから取り出し
● ハッシュ内にバッファを見つけたが、ロックされ
ていたケース
m1
m6
m0
FL
m2
15
6
7
44
92
41
14
2
29
13
35
58
20
42
72
…
…
…
…
16
ブロック番号72を取り出すとす
る。
ブロック72のバッ
ファが見つかっ
た!
でもロックされて
る
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
要求あり
インターフェース
● getblk バッファを割り当てて取得
● brelse バッファを自由リストに戻す
● bread ブロック読み込み
● breada ブロック先読み
● bwrite ブロック書き込み
brelse (1)
● ロックされたバッファを引数に呼び出される
● 戻り値はなし
● 自由リストのバッファ追加まちのプロセスを起こ
す
● 引数バッファのアンロックまちのプロセスを起こ
す
brelse (2)
● 割り込みを禁止して
● 有効なデータが入っていて、古くないバッファは
自由リストの最後に
● そうでない(IOエラーや遅延書き込み後)バッファ
は自由リストの先頭につなぐ
● 割り込み禁止を解除してバッファをアンロック
インターフェース
● getblk バッファを割り当てて取得
● brelse バッファを自由リストに戻す
● bread ブロック読み込み
● breada ブロック先読み
● bwrite ブロック書き込み
bread
● ブロック番号を引数にして、データを読み込んだ
バッファを返す
● バッファの取得にはgetblk()を使用
● バッファに有効なデータが乗っていればそのま
まもどし、乗ってなければディスクから読み込ん
で戻す
● シーケンシャルに読み込む場合は次のブロック
をバッファに載せたい→breada
インターフェース
● getblk バッファを割り当てて取得
● brelse バッファを自由リストに戻す
● bread ブロック読み込み
● breada ブロック先読み
● bwrite ブロック書き込み
breada
● ブロック番号を引数にして、データを読み込んだ
バッファを返す(breadと同じ)
● ただし、引数のブロック番号の次のブロックにも
非同期で読み込みをディスクに依頼する
● 详细は拟似コードで
インターフェース
● getblk バッファを割り当てて取得
● brelse バッファを自由リストに戻す
● bread ブロック読み込み
● breada ブロック先読み
● bwrite ブロック書き込み
bwrite
● バッファを引数にして、そのバッファのデータを
ディスクに書き込む
● 同期書き込みの場合はバッファのアンロック
brelse()まで実施
● 遅延書き込みの場合は「内容が古いフラグ」を
たてて、割り込みコンテキストからbrelse()でア
ンロックされた場合に自由リストの先頭に追加さ
れるようにする
まとめ&感想
● バッファキャッシュは最近アクセスしたものを
キャッシュに載せておく(LRU)
● 自由リストの順番が「長く使ってない順」そのも
の
● ハッシュと二重リンクリストの性質をうまく使って
いる
● コンテキストスイッチと割り込み怖い

More Related Content

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