0から作るOS開発 ヒープとkmallocとスラブアロケーターその1 概略
ヒープとkmallocとスラブアロケーター
今回はカーネルで使用するヒープ領域についてその実装方法である
スラブアロケーターを見ていきます。また、kmallocについての実装も
見て行きましょう!
ヒープとkmallocとスラブアロケーターその1 概略(現在のページ)
ヒープとkmallocとスラブアロケーターその2 実装
ヒープとkmallocとスラブアロケーターその3 改良
ヒープ(Heap)
ここで言うヒープはデータ構造でなくメモリーとしてのヒープ領域について
見ていきます。ヒープ領域は動的にメモリーを割り当てるため一定量のメモリー
を確保しておき、割り当て要求があったときに要求されたサイズのメモリーを
ヒープ領域から割り当てます。
動的メモリーの割り当て方式としていくつかの方式が適用できます。
固定長メモリーブロック
動的メモリーの割りて方式として最も単純な方式で、要求があったときに
固定長のメモリーブロックを要求サイズに応じて割り当て、空きブロックを
フリーリスト(空き領域を管理するリスト)で管理する方式があります。
この方式はメモリーの管理処理のコストも低く、実装もしやすいのですが、
大きな問題があります。メモリーの外部断片化が生じてしまうのです。
このようにフリーリストで固定長メモリーブロックで割り当て管理を
行っていると、メモリーの割り当て/解放を繰り返すことで、断片化が生じて
メモリー領域が実質余っている状態にも関わらず、割り当てすることが
できないという状態に陥ってしまいます。アプリケーションならこの様な
状態になってもまだ影響は少ないと思いますが、カーネルで発生してしまう
と影響が大きくなってきます。
そこで、断片化がなるべく発生しない方式について考えだされました。
バディ(相棒)システム
断片化がなるべく生じないようにバディシステムという方式があります。
この方式では、要求されたサイズに対して最小単位のメモリーブロックの
2の累乗分のサイズを割り当てます。(例えば、要求されたサイズがメモリー
ブロック7個分のサイズの場合、割り当てられるサイズはメモリーブロック
8個分のサイズとなります)。そして、要求されたサイズより割り当て可能な
メモリーブロックが連続している場合には、その割り当て可能領域を半分に、
更に半分にできる場合には半分に、そして要求サイズに適合するサイズと
なるまで繰り返します。反対に、メモリーが解放されると隣りの(相棒となる)
メモリーブロックが空きならばそのブロックと統合していきます。
このような方式では、空き領域を連結リストや木で管理を行いますので、
例えば、物理ページのような大きな単位のメモリー管理には向いていますが、
それより小さいサイズのメモリーを管理したいといったときには管理コストの
大きさが問題となってきます。
そこで、小さなサイズのメモリーに対する管理コストがある程度少なく、
メモリーの断片化が起こらないスラブアロケーターという方式が考えだされ
ました。
スラブアロケーター(Slab Allocator)
スラブアロケーターはSun Microsystems(2010年1月27日にオラクルに吸収合併)
のJeff Bonwickさんが開発されたメモリー割り当て方式でSunOS5.4に実装されました。
(興味有る方は
スラブアロケーターについての論文
について参照してください。
Jeff Bonwickさんの「The Slab Allocator:An Object-Caching
Kernel Memory Allocator」という論文となります)。
カーネルでは沢山のオブジェクトについて割り当てと解放を繰り返しています。
(オブジェクトととはメモリー上に配置した構造体のことだと思ってください)。
例えば、ファイルを探しているときには、lsコマンド使用してディレクトリーに
入っているファイル一覧を表示します。このときまだアクセスしたことのない
ディレクトリーではdentryなどファイルシステム関連のオブジェクトがヒープ領域
から割り当てられます。このときに一度に多くのオブジェクトの割り当てが
起こります。また、一方でしばらく参照していないディレクトリーについての
オブジェクト情報についてはもう使用しないものとして解放します。このように
カーネル内部では、オブジェクトの割り当て/解放が盛んに行われています。
ですので、オブジェクトの割り当て/解放処理がなるべく早くなるように、また
メモリーの断片化が起こらないようにする必要があります。
カーネルで使用するオブジェクトは、使用する(コンパイルした)時点でその
サイズが分かっていますので、固定サイズとなります。そして、オブジェクトは
固定サイズとなりますので、あらかじめ一定量の領域を確保(プール)しておく
ことができます。
(文章ばかりになってしまったので、あまり意味はありませんが絵を。。。)
例えば、先ほどの例で出て来ましたフリーリストやバディシステムによる領域
割り当てでは、あらかじめメモリーをプールしておくことが難しいので(実行時にしか
要求サイズがわかりませんので)、動的にメモリー割り当てを行います。動的に
割り当てを行うとどうしてもメモリーが断片化してしまうという問題をはらんで
しまいます。これに対して、スラブアロケーターでは小さいサイズのオブジェクト
については計算できる量のメモリーを確保して、断片化の問題を回避しようと
試みています。(なるべく静的にメモリーを確保することは、組み込みシステムでは
よく行われていますので、そのあたりからの発想だと思います。憶測です)。
スラブアロケーターは頻繁にオブジェクトの割り当てと解放を繰り返す場合に
うってつけの方法となります。
オブジェクトとスラブとキャッシュ
それではまず、スラブアロケーターで使用するオブジェクトとスラブとキャッシュに
ついて見ていきます。スラブアロケーターではメモリーの割り当て/解放を管理する
ために、”オブジェクト”とそのオブジェクトを複数個集めた”スラブ”、そして
スラブを複数個まとめた”キャッシュ”という単位にわけて考えます。
オブジェクト
オブジェクトはカーネルが使用する構造体(単に普通の変数でもかまいません)
となります。カーネルが実際にプログラムで使用するのが”オブジェクト”単位となり、
スラブアロケーターの割り当て/解放を行う単位もオブジェクト単位となります。
スラブアロケーターでは一番小さい括りとなります。
スラブ
オブジェクトを一つのグループに一括りにしたものが”スラブ”となります。スラブ
アロケーターではスラブ単位でスラブ内のオブジェクトは完全に割り当て済み 、
オブジェクトの一部が割り当て済み 、オブジェクトの全てが完全にフリー
といった状態管理を行います。(この管理は次に見る”キャッシュ”が担当します)。
キャッシュ
キャッシュは複数のスラブをまとめたものとなります。オブジェクト1種類につき
対応するキャッシュは1つとなります。なぜキャッシュと言うのかというと、Jeffさんの
論文からちょっと借用します。”オブジェクトキャッシングは頻繁にオブジェクトを
割り当て/解放を行うテクニックの1つとなります。キャッシングについては、オブジェクトの
初期状態(オブジェクトが作られたときの状態)から変更する必要の無い変数は
オブジェクトの割り当て・使用・解放、そして再度割り当てる間に変更しないで
そのままの値を保存しておく、ということが基本アイディアとなっています。すなわち、
オブジェクトを使用するたびにオブジェクトを生成したり、解放する度にオブジェクトの
メモリーを破壊(他の目的に再利用するために0などで初期化したりすること)する
必要は無いということです”。スラブで言うキャッシュと言うのはハードウェアでなく、
ソフトウェア的なキャッシュなんです。スラブアロケーターで言うキャッシュは1種類
の構造体が沢山集まったものと想像して頂ければと思います。。。
オブジェクトとスラブとキャッシュは次のように構成します。
例えば、inodeで1つのキャッシュを作り、dentryで1つのキャッシュを作ります。
1つの構造体につき対応するキャッシュは1つ作ります。
そして、割り当てるオブジェクトが
無くなった後に新しくオブジェクトの割り当て要求があった場合はスラブを新しく1つ増やして、
そこからオブジェクトを割り当てるようにします。
ここまでが、オブジェクトとスラブとキャッシュについてです。
ここまで見てきましたように、スラブアロケーターで管理する最大の単位は”キャッシュ”でした。
ですので、スラブアロケーターに対してアクセスするということは、”キャッシュ”に対してアクセス
するということになります。次は、その”キャッシュ”に対してアクセスするときにどのような
インターフェイスを作ればよいのかをJeffさんの論文から見ていきます。
スラブアロケーターのインターフェイス
ここまで見てきた内容を基にスラブアロケーターに対する要求を関数として実装します。
その関数(インターフェイス)を見ていきます。まず、スラブアロケーターへのアクセスはすなわち
”キャッシュ”を操作することでした。ですので、スラブアロケーターのインターフェイスはすなわち
キャッシュを操作する関数となります。キャッシュの操作について次のようになります。
このインターフェイスをフロントエンドといいます
スラブアロケーターのフロントエンド
”キャッシュ”の生成
割り当てるオブジェクトを生成するためにオブジェクトの情報を管理する”キャッシュ”を
まず作る必要があります。そして、キャッシュ生成時にオブジェクト生成に必要な情報
(名前、サイズ、アライメント、コンストラクターなど)を”キャッシュ”の管理情報に記録
しておきます。そして、”キャッシュ”生成時にはまだスラブの生成は行いません。スラブ
は、次に見るオブジェクト割り当て時に割り当てることができないオブジェクトが無いときに
初めて生成します。
”キャッシュ”の割り当て
”キャッシュ”の割り当て操作は、キャッシュから要求されたオブジェクトの割り当てます。
割り当てることができるオブジェクトが無い場合は、新たにスラブを生成してそこからオブジェクトを
割り当てます。スラブアロケーターのメモリー管理では、クライアント・サーバーモデルのように
クライアントはオブジェクトの割り当てや解放の要求を行い(そして、割り当て・解放処理が
速ければ速いほどよい)、アロケーターは陰ながらメモリーの管理を行います。
”キャッシュ”の解放
要求元が割り当てられたオブジェクトをスラブアロケーターに返還する操作となります。
このときオブジェクトは変更する必要のない変数はそのままの値で保存しておきます。
(ここがキャッシュと呼ばせる由縁でした。)
”キャッシュ”の破壊
破壊と大げさに書いておりますが、実際には生成したキャッシュ、スラブ(オブジェクト)に
割り当てられていたメモリー自体の解放を行います。これにより、キャッシュで使用していた
物理ページは完全に解放されて別の用途に使用することができるようになります。
そして、スラブアロケーターへの要求に対して、アロケーターは内部で次のような処理を
行います。このインターフェイスをバックエンドといいます。スラブアロケーターの実質的な
主体処理を行います。
スラブアロケーターのバックエンド
”キャッシュ”の拡大
キャッシュに割り当て可能なオブジェクトが無い場合に、ここでスラブを生成します。
”キャッシュ”の刈り取り
ここでは、不要になったスラブのメモリー領域を解放します。ある程度”全部解放済み”
のスラブが貯まった場合にも、他の用途にメモリーを使用できるようにスラブに割り当てて
いたメモリーを解放するようにします。
スラブアロケーターのインターフェイス関数
それでは、それぞれのインターフェイスについてその関数を見て行きたいと思います。
この関数は論文に記載されているものとなります。
”キャッシュ”の生成 kmem_cache_create
キャッシュの生成関数はkmem_cache_createとなります。
kmem_cache_createでキャッシュ構造体のメモリーを確保して、スラブ生成に
必要なオブジェクトの情報を登録します。
struct kmem_cache*
kmem_cache_create( char *name, size_t size, int align,
void ( constructor* )( void*, size_t ),
void ( destructor* )( void*, size_t ) );
”キャッシュ”の生成 kmem_cache_create
引数
説明
char *name
キャッシュの名前です。
size_t size
オブジェクト1つ分のサイズです。
int align
アライメントをバイト数で指定します。
指定したバイト境界にオブジェクトを配置します。
ハードウェアの制限があるなど特別な理由が無い限り通常0を指定します。
void ( *constructor )( void *, size_t )
コンストラクターです。
コンストラクターは関数でスラブ生成時にオブジェクトを初期化するためにコールバックします。
コンストラクターの引数void*はオブジェクトのアドレスが、
size_tにはオブジェクトのサイズが指定されて呼び出されます。
(Linuxでは引数size_tはありません。初期化関数登録元がコンパイル時にサイズがわかっているためです。
また、コンストラクターはオブジェクト1つにつき1回呼び出します。つまり、オブジェクト数分コールバックします)。
void ( *destructor )( void *, size_t )
ディストラクターです。
ディストラクターは関数でスラブ解放(破壊)時にオブジェクトの後処理をするためにコールバックします。
ディストラクターの引数void*はオブジェクトのアドレスが、
size_tにはオブジェクトのサイズが指定されて呼び出されます。
(Linuxではディストラクターはありません。確かに必要な場面が思いつかないですね)。
戻り値
説明
struct kmem_cache*
作成したキャッシュ構造体を返します。
”キャッシュ”の割り当て kmem_cache_alloc
キャッシュの割り当て関数はkmem_cache_allocとなります。
割り当てることができるオブジェクトが無い場合には、新しくスラブを生成します。
割り当てることができるオブジェクトが有る場合には、空いているスラブからオブジェクトを
割り当てます。
void*
kmem_cache_alloc( struct kmem_cache *cp, int flags );
”キャッシュ”の割り当て kmem_cache_alloc
引数
説明
struct kmem_cache *cp
オブジェクトを取得するキャッシュを指定します。
int flags
KM_SLEEP、KM_NOSLEEPなどのフラグを指定して、
割り当てることができるオブジェクトが無いときの振る舞いを指定します。
戻り値
説明
void*
割り当てたオブジェクトのアドレスを返します。
”キャッシュ”の解放 kmem_cache_free
キャッシュの解放関数はkmem_cache_freeとなります。
キャッシュにオブジェクトを返却します。
void
kmem_cache_free( struct kmem_cache *cp, void *buf );
”キャッシュ”の解放 kmem_cache_free
引数
説明
struct kmem_cache *cp
オブジェクトを返却するキャッシュを指定します。
void *buf
キャッシュに返却するオブジェクトのアドレスを指定します。
”キャッシュ”の破壊 kmem_cache_destroy
キャッシュの破壊関数はkmem_cache_destroyとなります。
スラブとオブジェクトに割り当てていたメモリーをすべて解放します。
void
kmem_cache_destroy( struct kmem_cache *cp );
”キャッシュ”の破壊 kmem_cache_destroy
引数
説明
struct kmem_cache *cp
スラブとオブジェクトのメモリーを解放するキャッシュを指定します。
”キャッシュ”の拡大 kmem_cache_grow
キャッシュの拡大関数はkmem_cache_growとなります。
kmem_cache_allocが呼び出されたときに、割り当て可能なオブジェクトが
無い場合に、物理メモリーを取得し、新しいスラブを作成します。
(バックエンドなので特に関数の入出力は規定しません)。
”キャッシュ”の刈り取り kmem_cache_reap
キャッシュの刈り取り数はkmem_cache_reapとなります。
kmem_cache_destroyが呼び出されたときや、物理メモリーが少なくなってきた
ときに、呼び出し使用していないスラブを解放します。
スラブアロケーターのインターフェース使用例
論文のフロントエンドを使用した簡単な例を見てみます。
今回の例はstruct inode構造体があったとしてそのキャッシュを作成し、
使用する例となります。
struct kmem_cache *cache_inode;
struct inode *inode;
/* 1.キャッシュを生成します */
cache_inode = kmem_cache_create( "inode_cache", 0, NULL, NULL );
/* 2.キャッシュからオブジェクトを割り当てます */
inode = ( struct inode* )kmem_caceh_alloc( cache_inode, 0 );
/* 3.inodeオブジェクトについて処理を行います */
/* ここで色々処理を行います */
/* 4.不要になったのでキャッシュを解放します */
kmem_cache_free( cache_inode, inode );
/* 5.ついでにキャッシュを壊します */
kmem_cache_destroy( cache_inode );
スラブ
スラブはオブジェクトを複数まとめたものでした。そして、スラブアロケーターの名前の
由縁となっているメモリーの単位となります。スラブにはオブジェクトが割り当てられて
いるか、フリーなのかを管理するkmem_bufctlを持っています。オブジェクトの割り当て
や解放を行う度にkmem_bufctlを更新していきます。kmem_bufctlは配列で
持っておき、空いているオブジェクトをフリーリストで管理していきます。スラブの管理
情報freeにはkmem_bufctlの先頭(または最後に解放されたオブジェクト)の
インデックスを格納しておき、割り当て時に参照することで目的のオブジェクトの
をすぐに取り出すことができます。
kmem_bufctlの定義
kmem_bufctlを定義していきます。kmem_bufctlは次に空いているオブジェクトの
リンク情報を入れるのでunsigned intの型にします。そして、次のように
KMEM_BUFCTL型を定義しておきます。そしてスラブで管理するkmem_bufctlは
KMEM_BUFCTL型の配列となります。
typedef UINT KMEM_BUFCTL;
スラブのレイアウト(小さなサイズのオブジェクトの場合)
通常スラブの領域として1ページを割り当てます。そして、1ページ(通常は4096バイト)
の中にスラブの管理情報とkmem_bufctlとオブジェクトを配置していきます。このとき
スラブの大きさはオブジェクトサイズによって変わってきます。スラブのサイズは次のように
計算できます。
スラブのサイズ = スラブの管理情報のサイズ +
( KMEM_BUFCTLのサイズ + オブジェクトのサイズ) × オブジェクト数
ここから、1ページは通常4096バイト(システムによって変わります)となりますので、
1ページのバイト数からスラブの管理情報のサイズを引いた残りをkmem_bufctlと
オブジェクトで使用できます。kmem_bufctlとオブジェクトは同じ数となりますので、
1ページに収めることができるオブジェクト数は、次のように決まります。
オブジェクト数 = ( 1ページのサイズ4096バイト - スラブの管理情報のサイズ ) /
( KMEM_BUFCTLのサイズ + オブジェクトのサイズ)
オブジェクトのサイズがある程度小さいと1ページのなかにある程度すっきり入りますので
スラブ管理情報とkmem_bufctlとオブジェクトを1ページ内に収めまります。これを
オンスラブ(On-slab) といいます。
(上記図は論文の内容とは異なります。論文では空きオブジェクトをkmem_bufctl
として使用するように、またスラブ管理情報はページの最後に配置するように記載されて
いますが、ここではLinuxの実装を参考にしました)。
上記図のように1ページにオブジェクトを収めますが、オブジェクトサイズは構造体に
よって変わります。そして、ちょうどすっきり1ページに収まることは稀で必ず
余り領域 が
出てきます。(この余り領域はいわゆるデッドスペースで何も活用されることはないので、
活用できることを思いつかれた方は何かに利用するとよいかもしれません。
スラブのレイアウト(大きなサイズのオブジェクトの場合)
先ほど見てきました余り領域はオブジェクトのサイズがある程度増えればその余り領域
も大きくなることがあります。例えば、1ページ4096バイトに対して、オブジェクトサイズは
2048バイトであった場合を考えてみると、1ページにスラブの管理情報とkmem_bufctl
を置く必要が有りましたので、オブジェクトは1つしか置けなくなります。このときの余り領域
は最大で、ほとんど2Kバイト近くが無駄な領域となってしまします。このため、”小さな”
オブジェクトに対して、”大きな”オブジェクトの管理方法を変える必要があります。
”大きな”オブジェクトに対しては、スラブの管理情報とkmem_bufctlは比較的小さな
データとなります。ですので、管理情報とは別のページに”大きな”オブジェクト専用の
ページを確保してそこに置きます。また、このとき管理情報とkmem_bufctlは”小さな”
オブジェクトとみなせますので、そのメモリーはオンスラブのオブジェクトから割り当てる
ようにします。(この辺りはまた後述します)。オブジェクトとスラブ管理情報を分離します
ので、これを
オフスラブ(Off-slab) といいます。
オンスラブとオフスラブを判断するにはオブジェクトのサイズを見て判断します。
オブジェクトサイズがページの1/8より小さい場合はオンスラブ、ページの1/8
以上のオブジェクトの場合はオフスラブにします。
オフスラブのスラブのサイズは次のように計算します。
オブジェクト用のページサイズ = オブジェクトのサイズ × オブジェクト数
このときオブジェクト用のページ数は2の累乗になるようにしておきます。(後に物理ページ
割り当てにバディシステムを組み込む場合そのようにしておいたほうが変更がすくなるなる
と思います。。。その辺りはどうしようが自由です)。
上記で2の累乗に収まるオブジェクト数が決まりますので、オフスラブから割り当てるスラブの
管理情報とkmem_bufctlのサイズは次のように計算します。
オフスラブのスラブサイズ = スラブの管理情報のサイズ +
( kmem_bufctlのサイズ × オブジェクト数 )
スラブの管理
オブジェクトの割り当て状況により、キャッシュでスラブ単位の管理を行います。
キャッシュではキャッシュ内のスラブを循環双方向連結リストで連結します。
(リストについては
ドライバーその他 補足説明 カーネルライブラリー リスト
を
参照してください。)
各リストは
オブジェクトは完全に割り当て済みのスラブ
オブジェクトの一部が割り当て済みのスラブ
オブジェクトの全てが完全にフリーのスラブ
の3つあります。そして、オブジェクトの割り当て時には、まず最初に”オブジェクトの
一部が割り当て済みのスラブ”のリストに連結されているスラブからオブジェクトを
割り当てるようにします。このとき一部割り当てのリストにスラブが連結されていない
場合には、”オブジェクトの全てが完全にフリーのスラブ”のリストに連結されている
スラブからオブジェクトを割り当てます。割り当て後は一部割り当て済みとなります
ので、”完全にフリー”のリストからスラブを削除して、”一部が割り当て”のリストに
スラブを追加します。また、オブジェクトを割り当てる際に、”完全にフリー”のリスト
にオブジェクトが無い場合は、この段階でスラブを生成するようにします。また、
オブジェクトの解放を行うときも、”完全に割り当て済み”リストからスラブを削除して
”一部が割り当済み”リストに追加したり、解放後完全にフリーである場合には
”完全にフリー”リストにスラブを移動させます。
スラブの定義
ここまでの内容で一旦スラブの定義を行っていきます。
/*
=================================================================================
Description : Definition of Slab
=================================================================================
*/
typedef struct slab SLAB;
struct slab
{
LIST list_slab; /* list of slabs */
ULONG color_offset; /* offset of the first object */
VOID *s_mem; /* pointer to objects */
UINT count; /* count of used objects in a slab */
KMEM_BUFCTL free; /* first free index */
};
SLAB構造体
メンバー
説明
LIST list_slab
キャッシュで管理する”完全に割り当て済み”、”一部割り当て済み”、”完全にフリー”リストに連結するノードです。
ULONG color_offset
カラーオフセットです。今回カラーリングの実装については説明しておりませんが、カラーリングについては後述します。
VOID *s_mem
スラブ内の最初のオブジェクトへのアドレスを格納します。
UINT count
現在使用中のオブジェクト数が格納されます。
KMEM_BUFCTL free
最後に解放されたオブジェクトのインデックス値が格納されます。
(スラブ生成時は一番最初のオブジェクトのインデックス値を格納します)。
ここで、color_offsetが出てきました。これはカラーリングを行うときに使用します。
今回はカラーリングについての実装は見合わせておりますが、説明自体は後述しています。
cout変数には使用中のオブジェクト数を入れます。countが0のときのみそのスラブに
割り当てられているページを解放するようにします。free変数にはフリーなオブジェクトの
インデックス値を入れます。オブジェクト割り当て時にはこの変数を参照するようにします。
最後に、*s_memですが、これにはオブジェクトのアドレスを格納します。
また、スラブ管理情報の構造体はスラブのサイズを計算するために、sizeofを使用
するためと、また32ビット環境では4バイト境界のアクセスの方が速くアクセスできるため
4バイトの倍数で構成しておきます。
キャッシュ
それでは、スラブを管理するキャッシュについてその管理情報を見ていきます。
キャッシュには先ほども見てきましたように、スラブをリストで管理していました。
そして、新しくスラブを生成するためにオブジェクトについての情報もキャッシュで
持っておく必要があります。これらの情報はkmem_cache_createでキャッシュ
生成時にキャッシュに登録します。
キャッシュが管理している情報については、例えばLinuxですと/proc/slabinfo
を見れば現在のキャッシュの状態を見ることができます。Linuxをご使用の方は
次のようにコマンドを入力することで内容を表示させることができます。
$cat /proc/slabinfo
または
$less /proc/slabinfo
とすると次のように表示されます。実際に表示されるデータ数が多いので、抜粋して
記載しています。また表示幅の広いので、まずは前半の列を下記します。
(ここでは、Ubuntuでの表示例の一部となります)。
# name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab>
net_namespace 0 0 1600 10 4
shmem_inode_cache 858 858 368 11 1
Acpi-State 1615 1615 48 85 1
taskstats 48 48 328 12 1
proc_inode_cache 1152 1177 368 11 1
kmalloc-8192 68 68 8192 4 8
kmalloc-4096 73 120 4096 8 8
kmalloc-2048 328 328 2048 8 4
kmalloc-1024 296 296 1024 8 2
kmalloc-512 1060 1072 512 8 1
kmalloc-256 377 400 256 16 1
kmalloc-128 1058 1152 128 32 1
kmalloc-64 3584 3584 64 64 1
kmalloc-32 4608 4608 32 128 1
kmalloc-16 3584 3584 16 256 1
kmalloc-8 13312 13312 8 512 1
kmalloc-192 7504 7896 192 21 1
kmalloc-96 1218 1218 96 42 1
kmem_cache 32 32 128 32 1
左の列から、”キャッシュの名前”、”使用中オブジェクト数”、”オブジェクト総数”、
”オブジェクトサイズ”、”スラブ毎のオブジェクト数”、”スラブ毎に必要なページ数”と
なります。ここで、一番上のnet_namespaceはアクティブオブジェクトが0、オブジェクト
数が0となっていますので、キャッシュを生成しただけでまだオブジェクトの割り当て
操作を行っていない、つまりまだスラブを生成していない状態となります。また、スラブ毎に
必要なページ数を見て頂くと1より大きいもの(オブジェクトサイズが512より大きい)
はオフスラブで、1のものはオンスラブとなります。
続いて後半の列です。(ここでは、tunableが表示されていましたが割愛させて
頂きました)。
# name : slabdata <active_slabs> <num_slabs> <sharedavail>
net_namespace : slabdata 0 0 0
shmem_inode_cache : slabdata 78 78 0
Acpi-State : slabdata 19 19 0
taskstats : slabdata 4 4 0
proc_inode_cache : slabdata 107 107 0
kmalloc-8192 : slabdata 17 17 0
kmalloc-4096 : slabdata 15 15 0
kmalloc-2048 : slabdata 41 41 0
kmalloc-1024 : slabdata 37 37 0
kmalloc-512 : slabdata 134 134 0
kmalloc-256 : slabdata 25 25 0
kmalloc-128 : slabdata 36 36 0
kmalloc-64 : slabdata 56 56 0
kmalloc-32 : slabdata 36 36 0
kmalloc-16 : slabdata 14 14 0
kmalloc-8 : slabdata 26 26 0
kmalloc-192 : slabdata 376 376 0
kmalloc-96 : slabdata 29 29 0
kmem_cache : slabdata 1 1 0
左から”キャッシュの名前”、”使用中スラブ”、”スラブの総数”、”共有利用可”
となります。こちらの内容はアドバンスな内容となりますので今回の説明は割愛
させて頂きます。
キャッシュではこのような内容を持っている必要があります。
では、キャッシュ kmem_cacheの定義について見て行きましょう!
キャッシュ kmem_cacheの定義
キャッシュの定義を見て行きましょう。
/*
==================================================================================
Description : Cache Management kmem_cache
==================================================================================
*/
struct kmem_cache
{
/* ------------------------------------------------------------------------ */
/* kmem cache management */
/* ------------------------------------------------------------------------ */
CONST CHAR *name; /* name of the cache */
LIST list; /* list of kmem caches */
/* ------------------------------------------------------------------------ */
/* slab attributes */
/* ------------------------------------------------------------------------ */
UINT slab_size; /* size of slab */
INT align; /* alignment */
SIZE_T color_num; /* number of colors */
UINT color_align; /* color alignment offset */
UINT color_next; /* next color */
UINT flags; /* slab flags */
VOID ( *constructor )( VOID *obj );
/* ------------------------------------------------------------------------ */
/* list of slab management */
/* ------------------------------------------------------------------------ */
LIST list_partial; /* partialy used slabs */
LIST list_full; /* fully used slabs */
LIST list_free; /* completely free slabs */
UINT free_limit; /* upper limit of free slabs */
/* ------------------------------------------------------------------------ */
/* object attributes */
/* ------------------------------------------------------------------------ */
SIZE_T obj_size; /* size of a object */
UINT obj_num; /* number of objects per slab */
UINT total_obj_num; /* total number of objects */
};
typedef struct kmem_cache KMEM_CACHE;
キャッシュ kmem_cache
メンバー
説明
CONST CHAR *name
キャッシュの名前です。コンパイル時に名前が決まっているので、constとなります。
LIST list
全てのキャッシュを連結するリストです。
キャッシュの状態を表示するときに、このリストを辿っていって全てのスラブについての情報を見ていく時などに使用します。
UINT slab_size
スラブのサイズです。
オンスラブのときは”スラブの管理情報”+”kmem_bufctl領域”+”オブジェクト領域”のサイズとなります。
オフスラブのときは”スラブの管理情報”+”kmem_bufctl領域”のサイズとなります。
INT align
kmem_cache_createで指定するalignを保存しておきます。
SIZE_T color_num
カラーリングで使用するカラーの総数を格納します。
UINT color_align
カラーリングで物理キャッシュを有効活用するためのオフセット値を格納します。
UINT color_next
次に生成するスラブのカラーを格納します。
UINT flags
スラブに関する各種フラグを格納します。
LIST list_partial
”オブジェクトの一部が割り当て済みのスラブ”を管理する連結リストです。
(
ダミーノード
となります)
LIST list_full
”オブジェクトの全部が割り当て済みのスラブ”を管理する連結リストです。
(
ダミーノード
となります)
LIST list_free
”オブジェクトの全てが完全にフリーのスラブ”を管理する連結リストです。
(
ダミーノード
となります)
UINT free_limit
キャッシュが持てる最大の”オブジェクト全部が解放済み”スラブ数です。
SIZE_T obj_size
オブジェクト1つのサイズを格納します。
UINT obj_num
1つのスラブで持つことができるオブジェクト数を格納します。
UINT total_obj_num
現在キャッシュがプールしているオブジェクト総数を格納します。
(カラーリングについては今回実装を見て行きませんが、説明は後述致します)。
ディストラクターについては使用しないつもりですので、今回定義していません。
リンク
© Copyright by Yabusame All Rights Reserved.
This picture is powered by harupu 2016,
http://sagittarius.dip.jp/~toshi/index.php