如何在Linux中为内存映射文件提供写时扩展功能? [英] How to provide extend-on-write functionality for memory mapped files in Linux?
问题描述
I'm working on porting some code from AIX to Linux. Parts of the code use the shmat()
system call to create new files. When used with SHM_MAP
in a writable mode, one can extend the file beyond its original length (of zero, in my case):
将文件映射到段时,通过访问该段来引用该文件.内存分页系统会自动处理物理I/O.超出文件末尾的引用会导致文件以页面大小的增量进行扩展.该文件不能扩展到下一个段边界之外.
When a file is mapped onto a segment, the file is referenced by accessing the segment. The memory paging system automatically takes care of the physical I/O. References beyond the end of the file cause the file to be extended in page-sized increments. The file cannot be extended beyond the next segment boundary.
(AIX中的段"是256 MB的地址空间块,而页面"通常是4 KB.)
(A "segment" in AIX is a 256 MB chunk of address space, and a "page" is usually 4 KB.)
我在Linux上希望要做的事情如下:
What I would like to do on Linux is the following:
- 保留很大一部分地址空间(不必大到256 MB,这些不是很大的文件)
- 设置页面保护位,以便在第一次访问之前未触及的页面时生成段错误
- 在发生页面错误时,清除引起页面错误"位并为该页面分配已提交的内存,以允许导致该页面错误的写入(或读取)继续进行
- 关闭共享存储区后,将修改后的页面写入文件
我知道我可以在Windows上使用结构化异常处理程序.在Linux上执行相同操作的对应方法是什么?也许有更好的方法可以在Linux上实现这种写时扩展功能?
I know I can do this on Windows with the VirtualProtect function, the PAGE_GUARD
memory protection bit, and a structured exception handler. What is the corresponding method on Linux to do the same? Is there perhaps a better way to implement this extend-on-write functionality on Linux?
我已经考虑过:
- 使用具有一定固定大小的
mmap()
,但是我无法确定应用程序代码写入了多少文件 - 分配一个很大的匿名共享内存区域,但是我仍然无法确定已经写入了多少区域
-
mmap()
本身似乎没有提供任何工具来扩展备份文件的长度
- using
mmap()
with some fixed large-ish size, but I can't tell how much of the file was written to by the application code - allocating an anonymous shared memory area of large-ish size, but again I can't tell how much of the area has been written
mmap()
by itself does not seem to provide any facility to extend the length of the backing file
自然,我希望对应用程序代码进行最少的更改.
Naturally I would like to do this with only minimal changes to the application code.
推荐答案
非常类似于我曾经做过的作业.基本上,我有一个页面"列表和一个框架"列表以及相关信息.使用SIGSEGV
,我将捕获故障并根据需要更改内存保护位.我将提供您可能会觉得有用的部分.
This is very similar to a homework I once did. Basically I had a list of "pages" and a list of "frames", with associated information. Using SIGSEGV
I would catch faults and alter the memory protection bits as necessary. I'll include parts that you may find useful.
int w_create_mapping(size_t size, void **addr)
{
*addr = mmap(NULL,
size * w_get_page_size(),
PROT_NONE,
MAP_ANONYMOUS | MAP_PRIVATE,
-1,
0
);
if (*addr == MAP_FAILED) {
perror("mmap");
return FALSE;
}
return TRUE;
}
安装信号处理程序
int w_set_exception_handler(w_exception_handler_t handler)
{
static struct sigaction sa;
sa.sa_sigaction = handler;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGSEGV);
sa.sa_flags = SA_SIGINFO;
if (sigaction(SIGSEGV, &sa, &previous_action) < 0)
return FALSE;
return TRUE;
}
异常处理程序
static void fault_handler(int signum, siginfo_t *info, void *context)
{
void *address; /* the address that faulted */
/* Memory location which caused fault */
address = info->si_addr;
if (FALSE == page_fault(address)) {
_exit(1);
}
}
加强保护
int w_protect_mapping(void *addr, size_t num_pages, w_prot_t protection)
{
int prot;
switch (protection) {
case PROTECTION_NONE:
prot = PROT_NONE;
break;
case PROTECTION_READ:
prot = PROT_READ;
break;
case PROTECTION_WRITE:
prot = PROT_READ | PROT_WRITE;
break;
}
if (mprotect(addr, num_pages * w_get_page_size(), prot) < 0)
return FALSE;
return TRUE;
}
我无法公开提供所有内容,因为团队很可能会再次使用相同的作业.
I can't publicly make it all available since the team is likely to use that same homework again.
这篇关于如何在Linux中为内存映射文件提供写时扩展功能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!