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)