简体   繁体   English

奇怪的 mmap 行为

[英]strange mmap behavior

I am making an experiment where I have a program that creates a shared file of 16 bytes, and then every second shows the value of each byte.我正在做一个实验,我有一个程序可以创建一个 16 字节的共享文件,然后每秒显示每个字节的值。

I want to be able to change the file while the program is running, and have the program recognize the changes.我希望能够在程序运行时更改文件,并让程序识别更改。

The program seems to indeed have writing to the memory write to the file as well;该程序似乎确实也将写入内存写入文件; but if I modify the file at runtime, it continues using old values despite the file being changed, and the file stops being updated.但是如果我在运行时修改文件,尽管文件被更改,它仍会继续使用旧值,并且文件将停止更新。

Here is the version that doesn't recognize changes.这是无法识别更改的版本。

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>

// | bag of global state.
struct {
    uint8_t *state;
    char *filename;
    int length;
} global = {
    (uint8_t *)0,
    "state.bin",
    16
};

// | clean up when ctrl-c is pressed.
static void onSIGINT(int unused)
{
    puts("caught SIGINT; cleaning up.");
    munmap(global.state, global.length);
    unlink(global.filename);
    exit(0);
}

// | display each byte of global shared memory
static void inspect()
{
    int i;

    for (i = 0; i < global.length; ++i) {
        printf("state[%d] = %d.\n", i, global.state[i]);
    }
}

int main(int argc, char **argv)
{
    /* anonymous scope: initialize shared memory */
    {
        // | mmap arguments
        void *addr = (void *)0;
        int prot = PROT_READ | PROT_WRITE;
        int flags = MAP_SHARED;
        int offset = 0;
        int fd = open(global.filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);

        // | initialize memory to 16 zerod out bytes.
        uint8_t dummy[16];
        memset(&dummy, 0, global.length);
        write(fd, dummy, global.length);

        global.state = (uint8_t *)mmap (
            addr,
            global.length,
            prot,
            flags,
            fd,
            offset
        );

        if (global.state == MAP_FAILED) {
            close(fd);
            perror("could not create map.\n");
            unlink(global.filename);
        }                
    }

    signal(SIGINT, onSIGINT);

    /* anonymous scope: mainloop */
    {
        int count = 0;

        for (;;) {
            system("clear");
            printf("refresh number: %d.\n", count);
            ++count;
            inspect();
            sleep(1);
        }
    }

    return 0;
}

Here is the version that also increments each byte on each display iteration to show that it is actually using the shared file.这是在每次显示迭代时也会增加每个字节的版本,以表明它实际上正在使用共享文件。

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>

// | bag of global state.
struct {
    uint8_t *state;
    char *filename;
    int length;
} global = {
    (uint8_t *)0,
    "state.bin",
    16
};

// | clean up when ctrl-c is pressed.
static void onSIGINT(int unused)
{
    puts("caught SIGINT; cleaning up.");
    munmap(global.state, global.length);
    unlink(global.filename);
    exit(0);
}

// | prints length bytes starting at address given.
static void inspect()
{
    int i;

    for (i = 0; i < global.length; ++i) {
        printf("state[%d] = %d.\n", i, global.state[i]);
        ++global.state[i];
    }
}

int main(int argc, char **argv)
{
    /* anonymous scope: initialize shared memory */
    {
        // | mmap arguments
        void *addr = (void *)0;
        int prot = PROT_READ | PROT_WRITE;
        int flags = MAP_SHARED;
        int offset = 0;
        int fd = open(global.filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);

        // | initialize memory to 16 zerod out bytes.
        uint8_t dummy[16];
        memset(&dummy, 0, global.length);
        write(fd, dummy, global.length);

        global.state = (uint8_t *)mmap (
            addr,
            global.length,
            prot,
            flags,
            fd,
            offset
        );

        if (global.state == MAP_FAILED) {
            close(fd);
            perror("could not create map.\n");
            unlink(global.filename);
        }                
    }

    signal(SIGINT, onSIGINT);

    /* anonymous scope: mainloop */
    {
        int count = 0;

        for (;;) {
            system("clear");
            printf("refresh number: %d.\n", count);
            ++count;
            inspect();
            sleep(1);
        }
    }

    return 0;
}

Note that it successfully keeps incrementing each byte UNTIL the moment I change one of the bytes in vim.请注意,它成功地不断增加每个字节,直到我更改 vim 中的一个字节为止。 When I overwrite the file, it stops modifying the file, but continues counting from where it was regardless.当我覆盖文件时,它会停止修改文件,但无论如何都会继续从它所在的位置开始计数。

Why is it behaving this way;为什么会这样; and how can I make it behave as expected?我怎样才能让它按预期运行?

This is caused by the method vim uses to create backup files.这是由 vim 用来创建备份文件的方法引起的。 This is documented for thebackupcopy option and the typical default is to rename the original file and edit a copy.这是为备份复制选项记录的,典型的默认值是重命名原始文件并编辑副本。 This causes the memory mapped view to be associated with the backup and not the file being edited.这会导致内存映射视图与备份相关联,而不是与正在编辑的文件相关联。

You can see this happen if you check the inode for the file:如果您检查文件的 inode,您可以看到这种情况:

$ echo z>a && ls -i a
14551885 a
$ vim a
$ ls -i a
14551887 a

As you can see the inode is now different.如您所见,inode 现在不同了。 If you used python for instance to open and modify the file you can edit the file in-place and it will work as expected.例如,如果您使用 python 打开和修改文件,您可以就地编辑文件,它会按预期工作。

Example using python instead:使用 python 的示例:

$ ls -i a
14551886 a
$ python
Python 2.7.6 (default, Oct 26 2016, 20:30:19)
[GCC 4.8.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> f = open('a', 'r+')
>>> f.write('22')
>>> f.close()
>>>

$ ls -i a
14551886 a

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM