Ответ 1
Ваш лучший шанс, вероятно, состоит в mmap()
исходных данных в файл, а затем mmap()
тот же файл снова с помощью MAP_PRIVATE
.
У меня есть код, в котором я часто копирую большой блок памяти, часто после внесения в него только очень небольших изменений.
Я внедрил систему, которая отслеживает изменения, но я подумал, что было бы неплохо, если бы можно было сказать ОС, чтобы сделать "копирование на запись" в памяти, и пусть это касается только создания копии из тех частей, которые меняются. Однако, в то время как Linux делает копию на запись, например, когда fork() ing, я не могу найти способ контролировать это и делать это сам.
Ваш лучший шанс, вероятно, состоит в mmap()
исходных данных в файл, а затем mmap()
тот же файл снова с помощью MAP_PRIVATE
.
В зависимости от того, что именно вы копируете, постоянная структура данных может быть решением вашей проблемы.
Используемый механизм копирования на запись, например, fork() - это функция MMU (Unit Management Unit Unit), которая обрабатывает пейджинг памяти для ядра. Доступ к MMU является привилегированной операцией, т.е. Не может выполняться приложением пользовательского пространства. Я не знаю, какой API-адрес для копирования на запись экспортируется в пользовательское пространство.
(Опять же, я не являюсь гуру в API Linux, поэтому другие могут указывать на соответствующие вызовы API, которые я пропустил.)
Изменить: И вот, MSalters поднимается по этому поводу.; -)
Его проще реализовать copy-on-write на объектно-ориентированном языке, например С++. Например, большинство классов контейнеров в Qt являются copy-on-write.
Но если вы тоже можете это сделать в C, это еще какая-то работа. Когда вы хотите назначить свои данные новому блоку данных, вы не делаете копии, вместо этого вы просто скопируете указатель в узле обертки вокруг ваших данных. Вам необходимо отслеживать в своих блоках данных статус данных. Если вы сейчас что-то измените в своем новом блоке данных, вы сделаете "реальную" копию и измените статус. Вы, конечно, не можете больше использовать простые операторы типа "=" для назначения, вместо этого должны иметь функции (в С++ вы просто выполняете перегрузку оператора).
Более надежная реализация должна использовать контрольные счетчики вместо простого флага, я оставлю это вам.
Быстрый и грязный пример: Если у вас есть
struct big {
//lots of data
int data[BIG_NUMBER];
}
вам нужно реализовать функции назначения и getters/seters самостоятельно.
// 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;
}
Вам также нужно написать конструкторы и деструкторы. Я действительно рекомендую С++ для этого.
Вы можете открыть свою собственную память через /proc/ $PID/mem, а затем mmap() интересную ее часть с MAP_PRIVATE в другое место.
Вот рабочий пример:
#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;
}
Я немного сомневаюсь в том, что мне нужно включить раздел с комментариями для безопасности.