У меня есть сегмент памяти, который был получен через mmap
с MAP_ANONYMOUS
.
Как я могу выделить второй сегмент памяти того же размера, который ссылается на первый, и сделать как скопированную запись в Linux (рабочий Linux 2.6.36 на данный момент)?
Я хочу иметь тот же эффект, что и fork
, без создания нового процесса. Я хочу, чтобы новое отображение оставалось в том же процессе.
Весь процесс должен повторяться как на страницах происхождения, так и на копии (как будто родительский и дочерний будут продолжать fork
).
Причина, по которой я не хочу выделять прямую копию всего сегмента, заключается в том, что они имеют несколько гигабайт больших размеров, и я не хочу использовать память, которая может быть общей для копирования на запись.
Что я пробовал:
mmap
общий сегмент, анонимный.
При дублировании mprotect
он доступен только для чтения и создайте второе сопоставление с remap_file_pages
также для чтения.
Затем используйте libsigsegv
для перехвата попыток записи, вручную создайте копию страницы, а затем mprotect
для чтения и записи.
Есть трюк, но очень грязный. Я по существу реализую свою собственную виртуальную машину.
Печально mmap
ing /proc/self/mem
не поддерживается в текущей Linux, в противном случае отображение MAP_PRIVATE
могло бы сделать трюк.
Механизм копирования на запись является частью виртуальной машины Linux, должен быть способ использовать их без создания нового процесса.
В качестве примечания: Я нашел подходящую механику в Mach VM.
Следующий код компилируется в моей OS X 10.7.5 и имеет ожидаемое поведение:
Darwin 11.4.2 Darwin Kernel Version 11.4.2: Thu Aug 23 16:25:48 PDT 2012; root:xnu-1699.32.7~1/RELEASE_X86_64 x86_64 i386
gcc version 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#ifdef __MACH__
#include <mach/mach.h>
#endif
int main() {
mach_port_t this_task = mach_task_self();
struct {
size_t rss;
size_t vms;
void * a1;
void * a2;
char p1;
char p2;
} results[3];
size_t length = sysconf(_SC_PAGE_SIZE);
vm_address_t first_address;
kern_return_t result = vm_allocate(this_task, &first_address, length, VM_FLAGS_ANYWHERE);
if ( result != ERR_SUCCESS ) {
fprintf(stderr, "Error allocating initial 0x%zu memory.\n", length);
return -1;
}
char * first_address_p = first_address;
char * mirror_address_p;
*first_address_p = 'a';
struct task_basic_info t_info;
mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
task_info(this_task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
task_info(this_task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
results[0].rss = t_info.resident_size;
results[0].vms = t_info.virtual_size;
results[0].a1 = first_address_p;
results[0].p1 = *first_address_p;
vm_address_t mirrorAddress;
vm_prot_t cur_prot, max_prot;
result = vm_remap(this_task,
&mirrorAddress, // mirror target
length, // size of mirror
0, // auto alignment
1, // remap anywhere
this_task, // same task
first_address, // mirror source
1, // Copy
&cur_prot, // unused protection struct
&max_prot, // unused protection struct
VM_INHERIT_COPY);
if ( result != ERR_SUCCESS ) {
perror("vm_remap");
fprintf(stderr, "Error remapping pages.\n");
return -1;
}
mirror_address_p = mirrorAddress;
task_info(this_task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
results[1].rss = t_info.resident_size;
results[1].vms = t_info.virtual_size;
results[1].a1 = first_address_p;
results[1].p1 = *first_address_p;
results[1].a2 = mirror_address_p;
results[1].p2 = *mirror_address_p;
*mirror_address_p = 'b';
task_info(this_task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
results[2].rss = t_info.resident_size;
results[2].vms = t_info.virtual_size;
results[2].a1 = first_address_p;
results[2].p1 = *first_address_p;
results[2].a2 = mirror_address_p;
results[2].p2 = *mirror_address_p;
printf("Allocated one page of memory and wrote to it.\n");
printf("*%p = '%c'\nRSS: %zu\tVMS: %zu\n",results[0].a1, results[0].p1, results[0].rss, results[0].vms);
printf("Cloned that page copy-on-write.\n");
printf("*%p = '%c'\n*%p = '%c'\nRSS: %zu\tVMS: %zu\n",results[1].a1, results[1].p1,results[1].a2, results[1].p2, results[1].rss, results[1].vms);
printf("Wrote to the new cloned page.\n");
printf("*%p = '%c'\n*%p = '%c'\nRSS: %zu\tVMS: %zu\n",results[2].a1, results[2].p1,results[2].a2, results[2].p2, results[2].rss, results[2].vms);
return 0;
}
Мне нужен тот же эффект в Linux.