メモリ マップと SIGSEGV ハンドラの使用には少し問題があります。まず、mprotect()はasync-signal safeではありません。つまりmprotect()
、シグナル ハンドラーでの動作は保証されていません。次に、シグナル ハンドラと複数のスレッド間で必要な構造の同期は非常に複雑です (ただし、GCC __syncおよび/または__atomicビルトインを使用すると可能です)。シグナル ハンドラで標準のロック プリミティブを使用できないためです。幸いなことに、単純にシグナルハンドラから戻ります。カーネルは問題のある命令をスキップしないため、直後に同じシグナルが発生します。
私は匿名のプライベート未予約メモリ マップをテストするための小さなプログラムを作成し、マップを使用read()
しwrite()
て更新しました。問題は、シグナル ハンドラがマップを更新している間に、他のスレッドがマップにアクセスする可能性があることです。
現在アクティブな領域に一時ファイルを使用し、レコードがページ境界をまたぐときに部分的なレコードを保持するために前後に追加のページを使用すると、うまくいくと思います。
実際のデータ ファイルは、非公開の匿名で予約されていない、アクセスできないマップ ( PROT_NONE
、MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE
) で表されます。SIGSEGV シグナル ハンドラーは、そのマップへのアクセスをキャッチします。そのマップのページに位置合わせされた領域がマップ解除され、一時ファイル ( MAP_SHARED | MAP_FIXED | MAP_NORESERVE
) からマップされます。秘訣は、一時ファイルMAP_SHARED | MAP_NORESERVE
を別のメモリ領域に追加でマップ ( ) できることです。シグナル ハンドラは、マップ内の一時ファイルのマップを解除するだけで、変換中に他のスレッドが領域にアクセスするのを停止できます。データは、別のメモリ領域のライブラリ関数で引き続き使用できます (実際のデータ ファイルを使用read()
して読み書きできます)。(ページキャッシュからの)まったく同じページが使用されることを意味し、カーネルがそれらのためにスワップまたはRAMを予約しないことを意味します。write()
MAP_SHARED
MAP_NORESERVE
このアプローチは、スレッドとロックに関してはうまく機能するはずですが、非同期シグナルセーフではないmmap()
、munmap()
、およびという問題が依然としてあります。mremap()
ただし、アトミックにのみアクセスされるグローバル変数があり、アプリケーション/ライブラリコードが構造体やマップを変更している場合にシグナルハンドラーがすぐに戻る場合、これは信頼できるはずです。