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