Mmap()を使用してMAP_SHAREDフラグを設定し、固定サイズの構造体を既知のファイル名に関連付けることにより、mmapを使用してLinuxで実行されているCプログラムのプログラム状態の特定の部分の永続性を実装したいと思います。パフォーマンス上の理由から、msync()をまったく呼び出さない方がいいと思います。他のプログラムは、このファイルにアクセスしません。プログラムが終了して再起動すると、同じファイルが再度マップされ、処理が実行されて、終了前の状態が回復します。私の質問はこれです:ファイル記述子でmsync()を呼び出さない場合、カーネルは、プロセスがSIGKILLで終了した場合でも、メモリへのすべての更新がディスクに書き込まれ、その後回復可能であることを保証しますか?また、プログラムがmsync()を呼び出さない場合でも、カーネルが定期的にページをディスクに書き込むことによる一般的なシステムオーバーヘッドはありますか?
EDIT:データが書き込まれるかどうかの問題は解決しましたが、open()/ writeでこの問題を処理しようとすると、予期しないシステム負荷が発生するかどうかはまだわかりません。 ()/ fsync()であり、プロセスがKILL/SEGV/ABRT/etcにヒットした場合に、一部のデータが失われる可能性があるというリスクを負います。知識のある人がチャイムを鳴らすことを期待して、「linux-kernel」タグを追加しました。
私は怠惰ではなく、データがディスクに確実に書き込まれるかどうかという質問に、コードを書くことで答えることにしました。答えはそれが書かれるということです。
これは、mmapされたファイルにデータを書き込んだ後に突然自殺するプログラムです。
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
typedef struct {
char data[100];
uint16_t count;
} state_data;
const char *test_data = "test";
int main(int argc, const char *argv[]) {
int fd = open("test.mm", O_RDWR|O_CREAT|O_TRUNC, (mode_t)0700);
if (fd < 0) {
perror("Unable to open file 'test.mm'");
exit(1);
}
size_t data_length = sizeof(state_data);
if (ftruncate(fd, data_length) < 0) {
perror("Unable to truncate file 'test.mm'");
exit(1);
}
state_data *data = (state_data *)mmap(NULL, data_length, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE, fd, 0);
if (MAP_FAILED == data) {
perror("Unable to mmap file 'test.mm'");
close(fd);
exit(1);
}
memset(data, 0, data_length);
for (data->count = 0; data->count < 5; ++data->count) {
data->data[data->count] = test_data[data->count];
}
kill(getpid(), 9);
}
これは、前のプログラムが停止した後に結果のファイルを検証するプログラムです。
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
typedef struct {
char data[100];
uint16_t count;
} state_data;
const char *test_data = "test";
int main(int argc, const char *argv[]) {
int fd = open("test.mm", O_RDONLY);
if (fd < 0) {
perror("Unable to open file 'test.mm'");
exit(1);
}
size_t data_length = sizeof(state_data);
state_data *data = (state_data *)mmap(NULL, data_length, PROT_READ, MAP_SHARED|MAP_POPULATE, fd, 0);
if (MAP_FAILED == data) {
perror("Unable to mmap file 'test.mm'");
close(fd);
exit(1);
}
assert(5 == data->count);
unsigned index;
for (index = 0; index < 4; ++index) {
assert(test_data[index] == data->data[index]);
}
printf("Validated\n");
}
この質問に答えるLinusTorvaldsからのコメントを見つけました http://www.realworldtech.com/forum/?threadid=113923&curpostid=114068
マップされたページはファイルシステムキャッシュの一部です。つまり、そのページに変更を加えたユーザープロセスが停止しても、ページはカーネルによって管理され、そのファイルへのすべての同時アクセスはカーネルを経由します。プロセスはそのキャッシュから提供されます。一部の古いLinuxカーネルではそれが異なっていたため、一部のカーネルドキュメントではまだmsync
を強制するように指示されています。
編集:ありがとうRobHはリンクを修正しました。
私は混乱を増す何かを見つけました:
munmapは、マップされたオブジェクトには影響しません。つまり、munmapを呼び出しても、マップされた領域の内容はディスクファイルに書き込まれません。 MAP_SHARED領域のディスクファイルの更新は、メモリマップ領域に格納するときにカーネルの仮想メモリアルゴリズムによって自動的に行われます。
これは、UNIX®環境での高度なプログラミングからの抜粋です。
linuxのマンページから:
MAP_SHAREDこのマッピングを、このオブジェクトをマップする他のすべてのプロセスと共有します。リージョンへの保存は、ファイルへの書き込みと同等です。 msync(2)またはmunmap(2)が呼び出されるまで、ファイルは実際には更新されない場合があります。
2つは矛盾しているようです。 APUEは間違っていますか?
私はあなたの質問に対する非常に正確な答えを見つけられなかったので、もう1つ追加することにしました:
Linuxのマンページ情報が正しくないか、Linuxがひどく不適合です。 msync
は、変更がファイルの論理状態にコミットされているかどうか、または他のプロセスがmmap
またはread
を使用してファイルにアクセスしているかどうかとは関係ありません。変更を参照してください。これは純粋にfsync
の類似物であり、電源障害またはその他のハードウェアレベルの障害が発生した場合にデータの整合性を確保する目的を除いて、no-opとして扱う必要があります。