Can I do a copy-on-write memcpy in Linux?

后端 未结 6 1271
面向向阳花
面向向阳花 2020-12-01 02:47

I have some code where I frequently copy a large block of memory, often after making only very small changes to it.

I have implemented a system which tracks the chan

相关标签:
6条回答
  • 2020-12-01 03:22

    Its easier to implement copy-on-write in a object oriented language, like c++. For example, most of the container classes in Qt are copy-on-write.

    But if course you can do that in C too, it's just some more work. When you want to assign your data to a new data block, you don't do a copy, instead you just copy a pointer in a wrapper strcut around your data. You need to keep track in your data blocks of the status of the data. If you now change something in your new data block, you make a "real" copy and change the status. You can't of course no longer use the simple operators like "=" for assignment, instead need to have functions (In C++ you would just do operator overloading).

    A more robust implementation should use reference counters instead of a simple flag, I leave it up to you.

    A quick and dirty example: If you have a

    struct big {
    //lots of data
        int data[BIG_NUMBER];
    }
    

    you have to implement assign functions and getters/setters yourself.

    // assume you want to implent cow for a struct big of some kind
    // now instead of
    struct big a, b;
    a = b;
    a.data[12345] = 6789;
    
    // you need to use
    struct cow_big a,b;
    assign(&a, b);   //only pointers get copied
    set_some_data(a, 12345, 6789); // now the stuff gets really copied
    
    
    //the basic implementation could look like 
    struct cow_big {
        struct big *data;
        int needs_copy;
    }
    
    // shallow copy, only sets a pointer. 
    void assign(struct cow_big* dst, struct cow_big src) {
        dst->data = src.data;
        dst->needs_copy = true;
    }
    
    // change some data in struct big. if it hasn't made a deep copy yet, do it here.
    void set_some_data(struct cow_big* dst, int index, int data } {
        if (dst->needs_copy) {
            struct big* src = dst->data;
            dst->data = malloc(sizeof(big));
            *(dst->data) = src->data;   // now here is the deep copy
           dst->needs_copy = false;
       }
       dst->data[index] = data;
    }
    

    You need to write constructors and destructors as well. I really recommend c++ for this.

    0 讨论(0)
  • 2020-12-01 03:22

    The copy-on-write mechanism employed e.g. by fork() is a feature of the MMU (Memory Management Unit), which handles the memory paging for the kernel. Accessing the MMU is a priviledged operation, i.e. cannot be done by a userspace application. I am not aware of any copy-on-write API exported to user-space, either.

    (Then again, I am not exactly a guru on the Linux API, so others might point out relevant API calls I have missed.)

    Edit: And lo, MSalters rises to the occasion. ;-)

    0 讨论(0)
  • 2020-12-01 03:23

    Here's a working example:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/mman.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    #define SIZE 4096
    
    int main(void) {
      int fd = shm_open("/tmpmem", O_RDWR | O_CREAT, 0666);
      int r = ftruncate(fd, SIZE);
      printf("fd: %i, r: %i\n", fd, r);
      char *buf = mmap(NULL, SIZE, PROT_READ | PROT_WRITE,
          MAP_SHARED, fd, 0);
      printf("debug 0\n");
      buf[SIZE - 2] = 41;
      buf[SIZE - 1] = 42;
      printf("debug 1\n");
    
      // don't know why this is needed, or working
      //r = mmap(buf, SIZE, PROT_READ | PROT_WRITE,
      //  MAP_FIXED, fd, 0);
      //printf("r: %i\n", r);
    
      char *buf2 = mmap(NULL, SIZE, PROT_READ | PROT_WRITE,
        MAP_PRIVATE, fd, 0);
      printf("buf2: %i\n", buf2);
      buf2[SIZE - 1] = 43;
      buf[SIZE - 2] = 40;
      printf("buf[-2]: %i, buf[-1]: %i, buf2[-2]: %i, buf2[-1]: %i\n",
          buf[SIZE - 2],
          buf[SIZE - 1],
          buf2[SIZE - 2],
          buf2[SIZE - 1]);
    
      unlink(fd);
      return EXIT_SUCCESS;
    }
    

    I'm a little unsure of whether I need to enable the commented out section, for safety.

    0 讨论(0)
  • 2020-12-01 03:28

    Your best chance is probably to mmap() the original data to file, and then mmap() the same file again using MAP_PRIVATE.

    0 讨论(0)
  • 2020-12-01 03:33

    You should be able to open your own memory via /proc/$PID/mem and then mmap() the interesting part of it with MAP_PRIVATE to some other place.

    0 讨论(0)
  • 2020-12-01 03:37

    Depending on what exactly it is that you are copying, a persistent data structure might be a solution for your problem.

    0 讨论(0)
提交回复
热议问题