2.2. ページ I/O を避けるために mlock を使用
mlock および mlockall システムコールは、指定したメモリー範囲にロックし、そのメモリーをページングできないように指示します。つまり、物理ページがページテーブルエントリーに割り当てられると、そのページへの参照は常に高速になります。
mlock システムコールには、2 つのグループがあります。mlock および munlock は、特定のアドレス範囲のロックおよびロック解除を行います。mlockall および munlockall は、プログラム領域全体をロックまたはアンロックします。
mlock 慎重に検討し、注意して使用してください。アプリケーションが大きい場合や、大規模なデータドメインがある場合は、システムが他のタスクにメモリーを割り当てできない場合に mlock 呼び出しがスラッシュする可能性があります。
注記
mlock は常に注意して使用してください。これを過剰に使用すると、メモリー不足 (OOM) エラーが生じる可能性があります。アプリケーションの先頭には mlockall 呼び出しを付けないでください。アプリケーションのリアルタイム部分のデータとテキストのみがロックされることが推奨されます。
mlock は、このプログラムにページ I/O がないことを保証しません。これは、データがメモリー内に留まるが、同じページに留まることを確認するのに使用されます。move_pages やメモリー圧縮関数は、mlock を使用してもデータを移動できます。
重要
非特権ユーザーは、大きなバッファーで
mlockall または mlock を使用できるようにするために、CAP_IPC_LOCK 機能が必要です。詳細は capabilities(7) man ページを参照してください。
さらに、メモリーロックはページベースで作成され、スタックされないことを通知することが推奨されます。つまり、2 つの動的に割り当てられたメモリーセグメントが、
mlock または mlockall への呼び出し 2 回ロックされた同じページを共有する場合、一致するページの munlock への単一の呼び出し、または munlockall によってアンロックされます。そのため、この二重ロック/シングルロックの問題を防ぐために、アプリケーションがロック解除しているページを認識する必要があります。
double-lock/single-unlock の問題を軽減する最も一般的な 2 つの方法は次のとおりです。
- 割り当て済みおよびロックされたメモリー領域を追跡し、ページをロックする前にラッパー機能を作成すると、そのページにあるユーザー数 (割り当て) を確認します。これは、デバイスドライバーで使用されるリソースカウントの原則です。
- 同じページで二重ロックを防ぐために、ページサイズとアライメントを考慮して割り当てを実行します。
以下のコード例は、2 番目の代替を示しています。
mlock の最適な利用方法は、アプリケーションのニーズとシステムリソースによって異なります。すべてのアプリケーションには単一のソリューションはありませんが、以下のコード例は、メモリーバッファーを割り当て、ロックする関数の実装のスタートポイントとして使用できます。
例2.2 アプリケーションでの mlock の使用
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
void *
alloc_workbuf(size_t size)
{
void *ptr;
int retval;
/*
* alloc memory aligned to a page, to prevent two mlock() in the
* same page.
*/
retval = posix_memalign(&ptr, (size_t) sysconf(_SC_PAGESIZE), size);
/* return NULL on failure */
if (retval)
return NULL;
/* lock this buffer into RAM */
if (mlock(ptr, size)) {
free(ptr);
return NULL;
}
return ptr;
}
void
free_workbuf(void *ptr, size_t size)
{
/* unlock the address range */
munlock(ptr, size);
/* free the memory */
free(ptr);
}
この関数
alloc_workbuf はメモリーバッファーを動的に割り当ててロックします。メモリーの割り当ては、メモリー領域をページに合わせるために posix_memalig によって行われます。size 変数が小さい場合は、ページサイズよりも小さいと、通常の malloc 割り当てで残りのページを使用できます。ただし、この手法を安全に使用するために、通常の malloc 割り当てでは mlock 呼び出しを行うことができません。これにより、二重ロック/シングルアンロックの問題が回避されます。この関数 free_workbuf は、メモリー領域のロックを解除し、解放します。
mlock および mlockall の使用方法に加えて、MAP_LOCKED フラグで mmap を使用してメモリー領域の割り当て、ロックすることもできます。以下の例は、mmap を使用した前述のコードの実装です。
例2.3 アプリケーションでの mmap の使用
#include <sys/mman.h>
#include <stdlib.h>
void *
alloc_workbuf(size_t size)
{
void *ptr;
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKED, -1, 0);
if (ptr == MAP_FAILED)
return NULL;
return ptr;
}
void
free_workbuf(void *ptr, size_t size)
{
munmap(ptr, size);
}
メモリー
mmap をページベースで割り当てると、同じページにロックが 2 つ存在せず、二重ロック/シングルアンロックの問題を防ぐのに役立ちます。一方、size 変数がページサイズの倍数ではない場合は、残りのページが無駄になります。さらに、mmap によってロックされたメモリーの munlockall ロックを解除するための呼び出しが必要になります。
フットプリントが小さいアプリケーションのもう 1 つは、コードの時間機密領域を入力する前に、
mlockall を呼び出し、その後に時間機密領域の最後に munlockall を呼び出します。これにより、重要なセクションにおいてページングを減らすことができます。同様に、mlock は比較的静的または、ページ I/O なしでアクセスが必要な徐々に増大するデータリージョンで使用できます。
注記
詳細は、以下の man ページは本セクションに記載の情報に関連しています。
- capabilities(7)
- mlock(2)
- mlock(3)
- mlockall(2)
- mmap(2)
- move_pages(2)
- posix_memalign(3)
- posix_memalign(3p)