問題
アプリケーションでのマイナーなページフォールトの発生を減らしたりなくしたりしようとしている過程で、私は紛らわしい現象を発見しました。つまり、ページフォルトを防ぐために十分な措置を講じたと思っていたにもかかわらず、同じアドレスへの書き込みに対してマイナーなページフォルトを繰り返しトリガーしています。
バックグラウンド
hereのアドバイスに従って、mlockall
現在および将来のすべてのページをメモリにロックするように呼び出しました。
私の元のユースケース (かなり大きな配列が関係していました) では、こちらのアドバイスに従って、すべての要素 (または少なくともすべてのページ) に書き込むことにより、データを事前にフォールトしました。RT パッチを適用したカーネルを実行しているユーザーを対象としたアドバイスであることは理解していますが、書き込みを強制して COW を阻止し、ページングを要求するという一般的な考え方は引き続き適用できるはずです。
mlockall
マイナーなページ フォールトを防ぐために使用できると考えていました。マニュアル ページはメジャー フォールトが発生しないことを保証しているように見えますが、他のさまざまなリソース (上記など) では、マイナー ページ フォールトを防ぐためにも使用できると述べています。
カーネルのドキュメントもこれを示しているようです。たとえば、unevictable-lru.txtとpagemap.txtは、mlock()
'ed ページは不可避であるため、再利用には適していないと述べています。
それにもかかわらず、私はいくつかのマイナーなページフォールトを引き起こし続けました.
例
問題を説明するために、非常に簡素化された例を作成しました。
#include <sys/mman.h> // mlockall
#include <stdlib.h> // abort
int main(int , char **) {
int x;
if (mlockall(MCL_CURRENT | MCL_FUTURE)) abort();
while (true) {
asm volatile("" ::: "memory"); // So GCC won't optimize out the write
x = 0x42;
}
return 0;
}
ここでは、同じアドレスに繰り返し書き込みます。cat /proc/[pid]/status | awk '{print $10}'
初期化が完了してから長い間、マイナーなページフォールトが発生し続けることは (たとえば を介して) 簡単にわかります。
pfaults.stp
に含まれているスクリプトの修正版*を実行してsystemtap-doc
、各ページ フォールトの時刻、フォールトをトリガーしたアドレス、フォールトをトリガーした命令のアドレス、メジャー/マイナー、読み取り/書き込みを記録しました。スタートアップ および からの最初の障害の後mlockall
、すべての障害は同一x
でした。
連続するページフォールトの間隔には、顕著なパターンが見られます。ある特定の実行では、間隔は秒単位でした:
2, 4, 4, 4.8, 8.16, 13.87, 23.588, 40.104, 60, 60, 60, 60, 60, 60, 60, 60, 60, ...
これは (ほぼ) 指数関数的なバックオフのようで、絶対的な上限は 1 分です。
分離された CPU で実行しても影響はありません。より高い優先順位で実行することもありません。ただし、リアルタイムの優先度で実行すると、ページフォールトがなくなります。
質問
- この動作は予期されたものですか?
1a. タイミングの説明は? - これを防ぐことは可能ですか?
バージョン
3.13.0-24-generic
カーネルと Systemtap のバージョンで Ubuntu 14.04 を実行しています2.3/0.156, Debian version 2.3-1ubuntu1 (trusty)
。追加のフラグなしでコンパイルされたコードgcc-4.8
。ただし、最適化レベルは問題ではないようです (asm volatile
ディレクティブがそのまま残っている場合。そうでない場合、書き込みは完全に最適化されます)。
stap
関連性があれば、詳細 (正確なスクリプト、元の出力など) を喜んで含めます。
*実際には、vm.pagefault
カーネルと systemtap の私の組み合わせでは、カーネルの関数に存在しなくなった変数を参照していたため、プローブが壊れていましたhandle_mm_fault
が、修正は簡単でした)