mmap、msync和linux进程终止

mmap、msync和linux进程终止,c,linux,linux-kernel,C,Linux,Linux Kernel,我想使用mmap在Linux下运行的C程序中实现程序状态某些部分的持久化,方法是使用mmap()和MAP_共享标志集将固定大小的结构与已知的文件名相关联。出于性能原因,我不希望调用msync(),也不会有其他程序访问此文件。当我的程序终止并重新启动时,它将再次映射同一个文件,并对其进行一些处理,以恢复终止前的状态。我的问题是:如果我从未在文件描述符上调用msync(),内核是否会保证所有对内存的更新都会写入磁盘,并且即使我的进程以SIGKILL终止也可以恢复?此外,即使我的程序从不调用msync

我想使用mmap在Linux下运行的C程序中实现程序状态某些部分的持久化,方法是使用mmap()和MAP_共享标志集将固定大小的结构与已知的文件名相关联。出于性能原因,我不希望调用msync(),也不会有其他程序访问此文件。当我的程序终止并重新启动时,它将再次映射同一个文件,并对其进行一些处理,以恢复终止前的状态。我的问题是:如果我从未在文件描述符上调用msync(),内核是否会保证所有对内存的更新都会写入磁盘,并且即使我的进程以SIGKILL终止也可以恢复?此外,即使我的程序从不调用msync(),内核是否也会定期将页面写入磁盘,从而产生一般的系统开销


编辑:我已经解决了是否写入数据的问题,但我仍然不确定这是否会导致一些意外的系统加载过度,尝试使用open()/write()/fsync()处理此问题冒着进程被KILL/SEGV/ABRT/等攻击时可能丢失某些数据的风险,添加了一个“linux内核”标签,希望有知识渊博的人能加入进来。

根据手册

该文件实际上可能不存在 直到调用msync(2)或munmap()为止


因此,您需要确保至少在退出之前调用
munmap()

要么Linux手册页信息不正确,要么Linux严重不一致
msync
与更改是否提交到文件的逻辑状态无关,也与使用
mmap
read
访问文件的其他进程是否看到更改无关;它纯粹是fsync的一种模拟,除了在电源故障或其他硬件级故障的情况下确保数据完整性外,应将其视为不可操作。我决定减少懒惰,并通过编写一些代码来回答数据是否最终写入磁盘的问题。答案是它会被写下来

下面是一个程序,它在将一些数据写入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);
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
类型定义结构{
字符数据[100];
uint16_t计数;
}国家统计局数据;
常量字符*test_data=“test”;
int main(int argc,const char*argv[]{
int fd=打开(“test.mm”,O|RDWR | O|CREAT | O|TRUNC,(模式t)0700);
如果(fd<0){
perror(“无法打开文件'test.mm'”);
出口(1);
}
大小数据长度=大小(状态数据);
如果(ftruncate(fd,数据长度)<0){
perror(“无法截断文件'test.mm'”);
出口(1);
}
状态数据*数据=(状态数据*)mmap(空,数据长度,保护读取,保护写入,映射共享,映射填充,fd,0);
if(MAP_FAILED==数据){
perror(“无法映射文件'test.mm'”);
关闭(fd);
出口(1);
}
memset(数据,0,数据长度);
对于(数据->计数=0;数据->计数<5;++数据->计数){
数据->数据[数据->计数]=测试数据[数据->计数];
}
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");
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
类型定义结构{
字符数据[100];
uint16_t计数;
}国家统计局数据;
常量字符*test_data=“test”;
int main(int argc,const char*argv[]{
int fd=打开(“test.mm”,仅限Ordu);
如果(fd<0){
perror(“无法打开文件'test.mm'”);
出口(1);
}
大小数据长度=大小(状态数据);
状态数据*数据=(状态数据*)mmap(空,数据长度,保护读取,映射共享,映射填充,fd,0);
if(MAP_FAILED==数据){
perror(“无法映射文件'test.mm'”);
关闭(fd);
出口(1);
}
断言(5==数据->计数);
无符号索引;
对于(索引=0;索引<4;++索引){
断言(测试数据[索引]==数据->数据[索引]);
}
printf(“已验证\n”);
}

我从Linus Torvalds那里找到了回答这个问题的评论

映射的页面是文件系统缓存的一部分,这意味着即使对该页面进行更改的用户进程死亡,该页面仍由内核管理,并且由于对该文件的所有并发访问都将通过内核,其他进程也将从该缓存获得服务。 在一些旧的Linux内核中,它是不同的,这就是为什么一些内核文档仍然告诉强制
msync

编辑:谢谢RobH更正了链接

编辑:

Linux 4.15引入了一个新的标志MAP_SYNC,它可以保证一致性

具有此标志的共享文件映射保证 而一些内存则可写地映射到 在该过程中,它将在同一时间在同一文件中可见 即使在系统崩溃或重新启动后也会发生偏移

参考资料:

在页面中搜索地图\u同步


我发现一些事情让我更加困惑:

munmap不影响映射的对象,即,对munmap的调用 不会导致写入映射区域的内容 到磁盘文件。更新地图共享的磁盘文件 区域由内核的虚拟内存算法自动发生 当我们存储到内存映射区域时

本文摘自UNIX®环境中的高级编程

从linux手册页:

映射\与映射此映射的所有其他进程共享此映射 对象存储到区域相当于写入到区域 文件在msync(2)或 munmap(2)被调用。

两人看到了吗