実験はLinux、x8632ビットで行われます。
だから私のアセンブリプログラムで、私はする必要があると思います定期的に(たとえば、100000基本ブロックを実行した後は毎回).bssセクションの配列をメモリからディスクにダンプします。配列の開始アドレスとサイズは固定されています。配列は実行された基本ブロックのアドレスを記録します。サイズは現在16Mです。
私はいくつかのネイティブコードを書こうとしました memcpy
から。bssセクションをスタックに追加してから、ディスクに書き戻します。しかし、それは非常に面倒で、パフォーマンスとメモリ消費量が心配です。たとえば、スタックに非常に大きなメモリを割り当てるたびに...
だからここに私の質問があります、どうすれば効率的な方法でグローバルデータセクションからメモリをダンプできますか?私は十分に明確ですか?
回答:
回答№1は2まず第一に、 コードのこの部分をasmで記述しないでください、特に。最初はそうではありません。この部分を処理するC関数を記述し、asmから呼び出します。別の16MiBをダンプするときにのみ実行される部分をパフォーマンスチューニングする必要がある場合は、手動でチューニングできます。システムレベルのプログラミングは、システムコール(またはC stdio関数)からのエラーリターンをチェックすることです。 asmでそれを行うのは苦痛です。
明らかにあなた できる システムコールを行うことはCと比較して特別なことではないので、asmで何かを記述します。そして、おそらくCと比較して、asmで簡単な部分はありません。 MFENCE
ロックの周り。
とにかく、私はあなたがあなたのバッファで何をしたいのかについての3つのバリエーションに取り組みました:
- 同じバッファを所定の場所に上書きします(
mmap(2)
/msync(2)
) - バッファのスナップショットをファイルに追加します(いずれかを使用)
write(2)
またはおそらく機能しないゼロコピーvmsplice(2)
+splice(2)
アイディア。) - 古いバッファを書き込んだ後、新しい(ゼロ化された)バッファを開始します。
mmap(2)
出力ファイルのシーケンシャルチャンク。
インプレース上書き
毎回同じディスク領域を上書きしたいだけの場合は、 mmap(2)
ファイルを作成し、それを配列として使用します。 (コール msync(2)
定期的にデータをディスクに強制します。)mmapされたメソッドは、ファイルの一貫した状態を保証しません。ただし、書き込みは要求がない限りディスクにフラッシュされる可能性があります。何らかの保証でそれを回避する方法がある場合はIDK(つまり、バッファフラッシュを選択するだけではありません)。タイマーなどなので、ページは通常、 msync(2)
。)
スナップショットを追加する
ファイルにバッファを追加する簡単な方法は、次のようになります。 単に電話する write(2)
書きたいとき. write(2)
必要なことはすべて行います。プログラムがマルチスレッドの場合、システムコールの前にデータをロックし、後でロックを解除する必要がある場合があります。 writeシステムコールがどれだけ速く返されるかわかりません。カーネルがデータをページキャッシュにコピーした後にのみ返される可能性があります。
スナップショットが必要なだけで、すべてがバッファはアトミックトランザクションです(つまり、バッファは互いに一貫している必要がある値のペアではなく、常に一貫した状態にあります)。その場合、呼び出す前にロックを取得する必要はありません。 write(2)
。この場合、わずかなバイアスが発生します(カーネルが順番にコピーされると仮定すると、バッファーの最後のデータは、最初のデータよりもわずかに遅い時間からのものになります)。
IDKの場合 write(2)
ダイレクトIO(ゼロコピー、ページキャッシュのバイパス)を使用すると、より遅くまたはより速く戻ります。 open(2)
であなたのファイル O_DIRECT
, write(2)
通常は。
バッファのスナップショットを書き込んでから変更を続ける場合は、プロセスのどこかにコピーが必要です。または、MMUのコピーオンライトのトリック:
ゼロコピー追加スナップショット
ユーザーページのディスクファイルへのゼロコピー書き込みを行うためのAPIがあります。 Linuxの vmsplice(2)
そして splice(2)
この順序で、ページをページキャッシュにマップするようにカーネルに指示できます。なし SPLICE_F_GIFT
、コピーオンライトとして設定されていると思います。 (おっと、実際にはmanページには SPLICE_F_GIFT
、 以下 splice(2)
コピーする必要があります。したがって、コピーオンライトセマンティクスを取得するメカニズムがある場合はIDKです。)
カーネルがページへの書き込みを完了して解放できるようになるまで、ページのコピーオンライトセマンティクスを取得する方法があると仮定します。
さらに書き込むには、memcpyへのカーネルが必要になる場合がありますデータがディスクにヒットする1〜2ページ前ですが、バッファ全体のコピーを保存してください。ソフトページフォールトとページテーブル操作のオーバーヘッドは、書き込みがディスクにヒットして書き込み予定のページが解放されるまでの短期間にデータアクセスパターンが非常に空間的にローカライズされていない限り、とにかく価値がない可能性があります。 (このように機能するAPIは存在しないと思います。これは、ページがディスクに到達した直後に解放されるメカニズムがないためです。Linuxは、ページを引き継いでページキャッシュに保持したいと考えています。)
vmspliceを使用したことがないので、詳細が間違っている可能性があります。
同じメモリの新しいコピーオンライトマッピングを作成する方法がある場合は、おそらく mmap
スクラッチファイルの新しいマッピングを作成します(tmpfsファイルシステムでは、prob。 /dev/shm
)、それはあなたに長い間ロックを保持することなくあなたにスナップショットを得るでしょう。次に、スナップショットをに渡すことができます write(2)
、コピーオンライトページフォールトが多すぎる前に、できるだけ早くマップを解除します。
すべてのチャンクの新しいバッファー
書き込みのたびにゼロのバッファから始めても問題がない場合は、次のことができます。 mmap(2)
ファイルの連続するチャンクであるため、生成するデータは常に適切な場所にあります。
- (オプション)
fallocate(2)
書き込みパターンがシーケンシャルでない場合の断片化を防ぐために、出力ファイルにいくらかのスペースがあります。 mmap(2)
出力ファイルの最初の16MiBへのバッファ。- 正常に実行
- 次の16MiBに移動する場合:
- 他のスレッドがバッファを使用するのを防ぐためにロックを取得します
munmap(2)
あなたのバッファmmap(2)
ファイルの次の16MiB 同じアドレスに、したがって、新しいアドレスをライターに渡す必要はありません。これらのページは、POSIXの要求に応じて事前にゼロ調整されます(カーネルにメモリを公開させることはできません)。- ロックを解除します
おそらく mmap(buf, 16MiB, ... MAP_FIXED, fd, new_offset)
は、 munmap
/ mmap
ペア。 MAP_FIXED
古いものを捨てる mmap
重なっていることを確認します。これは、ファイル/共有メモリへの変更が破棄されることを意味するのではなく、実際のマッピングが変更されることを意味すると思います。 munmap
.
回答№2の場合は0
の2つの説明 スナップショットを追加する ピーターの答えからのケース。
1.なしで追加 O_DIRECT
ピーターが言ったように、あなたが使わないなら O_DIRECT
, write()
データがページキャッシュにコピーされるとすぐに戻ります。ページキャッシュがいっぱいになると、古いページがディスクにフラッシュされるまでブロックされます。
データを読み取らずに(すぐに)追加するだけの場合は、定期的に電話をかけることでメリットが得られます。 sync_file_range(2)
以前に書き込まれたページのフラッシュをスケジュールし、 posix_fadvise(2)
〜と POSIX_FADV_DONTNEED
すでにフラッシュされたページをページキャッシュから削除するフラグ。これにより、その可能性が大幅に低下する可能性があります write()
ブロックします。
2.追加する O_DIRECT
と O_DIRECT
, write()
通常、データがディスクに送信されるまでブロックされます(ただし、厳密には保証されていません。を参照してください。 ここに)。これは遅いので、ノンブロッキング書き込みが必要な場合は、独自のI / Oスケジューリングを実装する準備をしてください。
アーカイブできる利点は次のとおりです。より予測可能な動作(ブロックするタイミングを制御)と、アプリケーションとカーネルのコラボレーションによるメモリとCPUの使用量の削減。