カーネルは、ページフレームを割り当てて解放するときに多くのことを行う必要があり、これにより多くのクロックサイクルが消費されます。このため、解放するページをリンクリストからキャッシュできます。つまり、実際には解放しませんが、リンクリストでは、次にページが割り当てられるときに、リンクリストから直接取得されるため、ページフレームの解放と割り当ての作業負荷が大幅に軽減され、パフォーマンスが向上します。カーネルには、このようなリンクリスト(CPUごとの変数であるクイックリスト)があります。各CPUは、構造体クイックリストタイプの構造変数に対応します。キャッシュページフレームの管理は、この構造によって実現されます。構造体クイックリストの定義は次のとおりです。
struct quicklist {
void *page;
int nr_pages;
};
最初は、この構造体の操作を見ると少し混乱するかもしれませんが、なぜこのリンクリスト構造体に次のポインタがないのですか?次のポインタがページフレームの先頭に書き込まれていることがわかります。これは、スペースを節約するために行うことができます。
1)最初にページフレームのリリース操作を確認します
static inline void __quicklist_free(int nr, void (*dtor)(void *), void *p,
struct page *page)
{
struct quicklist *q;
q = &get_cpu_var(quicklist)[nr];
*(void **)p = q->page;
q->page = p;
q->nr_pages++;
put_cpu_var(quicklist);
}
最初の呼び出しでは、q-> pageの値はNULLです。この関数が複数回呼び出されると、次のリンクリストが形成されます。
2)ページフレームの割り当てコードを上図と組み合わせると、ページフレームが割り当てられると、前回リンクリストに追加されたノードがスタック:
static inline void *quicklist_alloc(int nr, gfp_t flags, void (*ctor)(void *))
{
struct quicklist *q;
void **p = NULL;
q =&get_cpu_var(quicklist)[nr];
p = q->page;
if (likely(p)) {
q->page = p[0];
p[0] = NULL;
q->nr_pages--;
}
put_cpu_var(quicklist);
if (likely(p))
return p;
p = (void *)__get_free_page(flags | __GFP_ZERO);
if (ctor && p)
ctor(p);
return p;
}