(cache) 0から作るOS開発 ヒープとkmallocとスラブアロケーターその1 概略
0から作るソフトウェア開発

日々勉強中。。。

0から作るOS開発

環境準備

環境設定

ブートローダ

カーネルローダ

GRUB

カーネル

ドライバーその他

0から作るOS開発 ヒープとkmallocとスラブアロケーターその1 概略

ヒープとkmallocとスラブアロケーター

今回はカーネルで使用するヒープ領域についてその実装方法である

スラブアロケーターを見ていきます。また、kmallocについての実装も

見て行きましょう!

  1. ヒープとkmallocとスラブアロケーターその1 概略(現在のページ)


  2. ヒープとkmallocとスラブアロケーターその2 実装


  3. ヒープと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などファイルシステム関連のオブジェクトがヒープ領域

から割り当てられます。このときに一度に多くのオブジェクトの割り当てが

起こります。また、一方でしばらく参照していないディレクトリーについての

オブジェクト情報についてはもう使用しないものとして解放します。このように

カーネル内部では、オブジェクトの割り当て/解放が盛んに行われています。


ですので、オブジェクトの割り当て/解放処理がなるべく早くなるように、また

メモリーの断片化が起こらないようにする必要があります。


カーネルで使用するオブジェクトは、使用する(コンパイルした)時点でその

サイズが分かっていますので、固定サイズとなります。そして、オブジェクトは

固定サイズとなりますので、あらかじめ一定量の領域を確保(プール)しておく

ことができます。

(文章ばかりになってしまったので、あまり意味はありませんが絵を。。。)

スラブアロケーターの例

例えば、先ほどの例で出て来ましたフリーリストやバディシステムによる領域

割り当てでは、あらかじめメモリーをプールしておくことが難しいので(実行時にしか

要求サイズがわかりませんので)、動的にメモリー割り当てを行います。動的に

割り当てを行うとどうしてもメモリーが断片化してしまうという問題をはらんで

しまいます。これに対して、スラブアロケーターでは小さいサイズのオブジェクト

については計算できる量のメモリーを確保して、断片化の問題を回避しようと

試みています。(なるべく静的にメモリーを確保することは、組み込みシステムでは

よく行われていますので、そのあたりからの発想だと思います。憶測です)。


スラブアロケーターは頻繁にオブジェクトの割り当てと解放を繰り返す場合に

うってつけの方法となります。

オブジェクトとスラブとキャッシュ

それではまず、スラブアロケーターで使用するオブジェクトとスラブとキャッシュに

ついて見ていきます。スラブアロケーターではメモリーの割り当て/解放を管理する

ために、”オブジェクト”とそのオブジェクトを複数個集めた”スラブ”、そして

スラブを複数個まとめた”キャッシュ”という単位にわけて考えます。



オブジェクトとスラブとキャッシュは次のように構成します。

スラブアロケーターの構造

例えば、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 現在キャッシュがプールしているオブジェクト総数を格納します。


(カラーリングについては今回実装を見て行きませんが、説明は後述致します)。

ディストラクターについては使用しないつもりですので、今回定義していません。


リンク

inserted by FC2 system