linux - 如何在 Linux 中为内存映射文件提供写时扩展功能?

标签 linux posix aix

我正致力于将一些代码从 AIX 移植到 Linux。部分代码使用 shmat() system call创建新文件。当在可写模式下与 SHM_MAP 一起使用时,可以将文件扩展到超过其原始长度(在我的例子中为零):

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 的地址空间 block ,而“页”通常是 4 KB。)

喜欢在 Linux 上做的事情如下:

  • 保留一大块地址空间(不必大到 256 MB,这些文件不是那么大)
  • 设置页面保护位,以便在第一次访问之前未被触及的页面时生成段错误
  • 在出现页面错误时,清除“导致页面错误”位并为该页面分配已提交的内存,允许导致页面错误的写入(或读取)继续进行
  • 关闭共享内存区域后,将修改后的页面写入文件

我知道我可以在 Windows 上使用 VirtualProtect 执行此操作函数、PAGE_GUARD 内存保护位和 structured exception handler . Linux 上相应的方法是什么?是否有更好的方法在 Linux 上实现这种写时扩展功能?

我已经考虑过:

  • 使用 mmap() 一些固定的大尺寸,但我无法判断应用程序代码写入了多少文件
  • 分配一个较大的匿名共享内存区域,但我还是不知道该区域有多少已被写入
  • mmap() 本身似乎没有提供任何工具来扩展支持文件的长度

当然,我希望只对应用程序代码进行最少的更改。

最佳答案

这与我曾经做过的作业非常相似。基本上我有一个“页面”列表和一个“框架”列表,以及相关信息。使用 SIGSEGV 我会捕获错误并根据需要更改内存保护位。我将包含您可能会觉得有用的部分。

创建映射。最初它没有权限。

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;
}

我不能公开提供所有内容,因为团队可能会再次使用相同的作业。

关于linux - 如何在 Linux 中为内存映射文件提供写时扩展功能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6935988/

相关文章:

linux - 在 crontab 中调度脚本时错误替换错误

`posix_spawn` 的能力

multithreading - 了解 POSIX 线程

linux - 如何将 echo/print 命令与另一个命令(如 cat 或 sed)结合使用

java - Java 应用程序中的 TCP 握手非常慢

linux - 使用 awk 变量删除文件

c++ - 非阻塞读取文件/获取文件描述符c++

linux - 我们怎么知道我们在自旋锁中?

c - 如果 close(2) 因 EIO 而失败,文件描述符是否仍会被删除?

linux - 使用 Linux 中特定内容行的名称批量重命名 pdf 文件