OSv malloc 日本語版

OSv malloc 日本語版

A briefly explanation of malloc() in OSv

842515eaf8fbb2dfcc75197e7797dc15?s=128

Satoru Takeuchi

October 12, 2017
Tweet

Transcript

  1. 2.

    はじめに • 本記事はOSv Advent Calendar[1] 21日目の記事 • 前提知識 ◦ OS/OSvについて、参考文献[2]程度の基礎知識

    ◦ C++の言語仕様 ◦ 基本的なアルゴリズム、データ構造の知識 ◦ Linux kernelのソースを読んだことがあれば、なおよい • 調査対象ソースは12/21日現在におけるupstreamのmaster branch[3] ◦ HEAD commit: f7d3ddd648b38789daa8287626a66863a780f139 ◦ 簡単のため、デバッグ機能やトレース機能、排他制御については無視 • 以後、パス名はOSv ソースコードのトップディレクトリからの相対パス
  2. 3.

    概要 • 定義はcore/mempool.cに存在: ファイルサイズは2K行弱 ◦ 実装はシンプル ◦ コメントにも”Our malloc() is

    very coarse”とか書いてたりする • 小さなobjectの取得に特化(JVMのためか?) • カーネル空間でもユーザ空間でも全く同じmalloc()を使う ◦ それがOSv way ◦ linux kernelでいうとアプリがkmalloc()を直接呼ぶイメージ • 実際のobject獲得sizeは2^n >= 8バイト(nは正の整数)に切り上げられる ◦ 例) 4byte要求すると8byteのobjectを獲得
  3. 4.

    構成 • memory objectのsizeに応じて異なる仕組みを利用 ◦ <= 1KiB: mempool ◦ >

    1KiB: page allocator user or kernel mempool (Linux kernelの sl[auo]bに相当) memory管理subsystem page allocator (Linux kernelの buddy allocatorに相当 malloc()/free()
  4. 5.

    簡単な実行の流れ malloc(size) - > std_malloc(size, align) (*1) sizeを2^n >= 8

    (nは正の整数)バイトに切り上げ if (1KiB以下の獲得 && SMP用初期化後) # 後者はアプリなら常に真 malloc_pools[lg(n)].alloc() # mempoolより割り当て else if (1KiB<size<=4KiBの割り当て) memory::alloc_page() # page allocatorより1pageを割り当て else malloc_larger() # page allocatorより複数pageを割り当て # 3つの中で一番複雑な論理(説明は割愛) *1) posix_memalign()などによって明にalignmentを指定した場合は、もう少し 込み入った条件分岐をする。詳細はstd_malloc()のコードを参照
  5. 6.

    1KiB以下の割り当て • 前述のようにmempoolを使用 ◦ class malloc_pool(class poolを継承)によって管理 • 定義: “malloc_pool

    malloc_pools[]” ◦ malloc()で獲得するobject sizeごとに存在するmempool ◦ 要素数はlg(page size)+1。x86_64ならpage sizeが4KiBなので12+1=13 ◦ それぞれsizeが1,2,4,8,...,4KiB,8KiBのsizeの割り当てに対応 ▪ 最後の8KiBの要素の必要性がよくわからない... ◦ アプリが使う範囲ではmempool[11,…,(page_size+1)]は未使用
  6. 7.

    mempoolのしくみ • slab allocator[4]のOSv版。page size以下の小さなsizeの割り当てに使用 ◦ class poolによって管理 # えらく汎用的な名前…

    • 8byteから4KiBまでのsizeのobjectを扱う ◦ size <=1KiB: 1page内に複数objectを配置 ◦ 1KiB < size <= 4KiB: 1page内に1object • MP環境におけるスケーラビリティ向上のためのper-CPU cacheを持つ ◦ 参照の局所性により、object獲得時に自CPUが最近使用した、つまり cacheに残っているmemoryを使える可能性が向上 ◦ CPUごとの領域だけ使っている限りは、排他制御不要 • これ以上の詳細は”Memory allocation strategy”で始まるcommentや、ソース コードを参照
  7. 8.

    page allocatorのしくみ • page sizeのmemory割り当て ◦ class page_range_allocatorによって管理 • L1,

    L2という2levelのcacheを持つ ◦ L1: per-CPU cache ◦ L2: global cache • cacheの下の最下層をglobal page allocatorと呼ぶ kernel subsystem (mempool含む) L1 cache (per-CPU cache) L2 cache (全CPUで共有) global page allocator page allocator
  8. 9.

    page allocator: L1 cache • per-CPUのcache • struct l1を用いて管理 ◦

    cacheするpage数 <=l1::max(512) • 定義: “l1 percpu_l1[<CPU数>]” • UI: 名前が*_localなら、内部で同期獲得/開放しない ◦ page獲得: l1::alloc_page{,_local} ◦ page開放: l1::free_page{,_local} • L2 cacheとのインターフェイス ◦ 非同期: per-CPU thread(“page_pool_l1_<cpu>”)を用いる ▪ 残数 < l1::max*¼ => 複数pageを獲得 ▪ 残数 > l1::max*¾ => 複数pageを開放 ◦ 同期: 残数が0, l1::maxになれば、それぞれ同期的に複数pageを獲得/開 放
  9. 10.

    page allocator: L2 cache • 全CPU共通のcache • class l2を用いて管理 ◦

    cacheするpage数は最多でl2::max) • (L1 cacheが使う)UI: 名前が”try_*”なら、内部で同期獲得/開放しない ◦ 複数page獲得: l2::{try_,}alloc_page_batch ◦ 複数page開放: l2::{try_,}free_page_batch • global page allocatorとのインターフェイス • 非同期: per-CPU thread(“page_pool_l2”)を用いる ◦ 残数 < l2::max*¼ => 複数pageを獲得 ◦ 残数 > l2::max*¾ => 複数pageを開放 ◦ 同期: 残数が0, l2::maxになれば、それぞれ同期的に複数pageを獲得/開 放
  10. 12.

    参考文献 1. OSv Advent Calendar2014 http://qiita.com/advent-calendar/2014/osv 2. OSvのご紹介 in OSC2014

    Tokyo/Fall, Takuya ASADA, Cloudius Systems http://www.slideshare.net/syuu1228/osv-in-osc2014-tokyofall 3. OSvのsource code https://github.com/cloudius-systems/osv 4. slab allocation at Wikipedia http://en.wikipedia.org/wiki/Slab_allocation 5. mallocの旅(Glibc編), こさき@ぬまづ http://www.slideshare.net/kosaki55tea/glibc-malloc
  11. 13.

    おまけ: OSvのコードを読んでみた感想 • malloc()の実装を読むはずが、結局カーネルのメモリ管理のコードをかなり読む はめに陥った。が、勉強になったのでよしとする • ソースがコンパクトで、今やすっかり巨大化したLinux kernelに比べると、はるか に読みやすい ◦

    まだまだコード最適化による性能の伸びしろがある ◦ 息抜き/勉強用に気軽に読めるのでOSの学習にいいかも • malloc(size)以外にmalloc(size, align)という、posix_memalign()に似た関数も ある アプリにはexportしていないため、kernelのみが使用可能 C++のoverload機能があったからできる。ビバC++