프로세스 내 쓰기 메모리에 복사본 할당
나는 메모리 세그먼트를 가지고 있는데, 그것은 다음을 통해 얻은 것입니다.mmap
와 함께MAP_ANONYMOUS
.
첫 번째 메모리 세그먼트를 참조하는 동일한 크기의 두 번째 메모리 세그먼트를 할당하고 Linux에서 두 개의 복사온 쓰기를 수행하려면 어떻게 해야 합니까(현재 Linux 2.6.36 작동 중)?
저는 정확히 같은 효과를 원합니다.fork
프로세스를 새로 만들지 않고도 가능합니다.저는 새로운 매핑이 동일한 프로세스를 유지하기를 원합니다.
사본 가능해야 .fork
).
전체 세그먼트의 복사본을 직접 할당하지 않는 이유는 해당 세그먼트가 수 기가바이트 크기이고 쓰기 시 복사가 공유될 수 있는 메모리를 사용하고 싶지 않기 때문입니다.
내가 시도한 것:
mmap
세그먼트 공유, 익명. 시 »mprotect
읽기 전용으로 두 번째 매핑을 만듭니다.remap_file_pages
읽기 전용입니다.
사용할 경우libsigsegv
쓰기 시도를 가로채려면 페이지를 수동으로 복사한 다음mprotect
읽기/쓰기 모두 가능합니다.
속임수를 쓰지만, 매우 더럽습니다.기본적으로 나만의 VM을 구현하고 있습니다.
도 슬게프mmap
잉/proc/self/mem
, 경우에는 Linux에서 지원되지 않습니다. 그렇지 않으면MAP_PRIVATE
지도를 그리면 속임수를 쓸 수 있습니다.
쓰기 시 복사 메커니즘은 Linux VM의 일부이므로 새로운 프로세스를 만들지 않고도 이를 활용할 수 있는 방법이 있어야 합니다.
참고로:Mach VM에서 적절한 메커니즘을 찾았습니다.
는 내 X 동작을 .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;
}
나는 리눅스에서도 같은 효과를 원합니다.
저는 동일한 작업을 수행하려고 노력했습니다(실제로는 실제 지역의 스냅샷만 생성하면 되므로 복사본을 생성할 필요가 없기 때문에 시각적으로 더 간단합니다).저는 이것에 대한 좋은 해결책을 찾지 못했습니다.
직접 커널 지원(또는 그 부족):모듈을 수정/추가하면 이를 달성할 수 있습니다.그러나 기존 COW 영역에서 새 COW 영역을 설정하는 간단한 방법은 없습니다.포크에서 사용하는 코드(copy_page_rank
복사하기vm_area_struct
프로세스/가상 주소 공간에서 다른 프로세스/가상 주소 공간(새 주소)으로 이동하지만 새 매핑의 주소가 이전 매핑의 주소와 동일하다고 가정합니다. "리맵", "리맵", "리맵/리맵"을 .vm_area_struct
주소 변환 포함.
BTRFS: 저는 이것을 위해 btrfs에 COW를 사용하려고 생각했습니다.저는 리플링크된 두 개의 파일을 매핑하는 간단한 프로그램을 작성하고 매핑을 시도했습니다.그러나 페이지 정보를 확인할 때는/proc/self/pagemap
파일의 두 인스턴스가 동일한 캐시 페이지를 공유하지 않음을 나타냅니다.(적어도 내 시험이 틀리지 않는 한).그래서 당신은 이것을 함으로써 많은 것을 얻지 못할 것입니다.동일한 데이터의 실제 페이지는 서로 다른 인스턴스 간에 공유되지 않습니다.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <inttypes.h>
#include <stdio.h>
void* map_file(const char* file) {
struct stat file_stat;
int fd = open(file, O_RDWR);
assert(fd>=0);
int temp = fstat(fd, &file_stat);
assert(temp==0);
void* res = mmap(NULL, file_stat.st_size, PROT_READ, MAP_SHARED, fd, 0);
assert(res!=MAP_FAILED);
close(fd);
return res;
}
static int pagemap_fd = -1;
uint64_t pagemap_info(void* p) {
if(pagemap_fd<0) {
pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
if(pagemap_fd<0) {
perror("open pagemap");
exit(1);
}
}
size_t page = ((uintptr_t) p) / getpagesize();
int temp = lseek(pagemap_fd, page*sizeof(uint64_t), SEEK_SET);
if(temp==(off_t) -1) {
perror("lseek");
exit(1);
}
uint64_t value;
temp = read(pagemap_fd, (char*)&value, sizeof(uint64_t));
if(temp<0) {
perror("lseek");
exit(1);
}
if(temp!=sizeof(uint64_t)) {
exit(1);
}
return value;
}
int main(int argc, char** argv) {
char* a = (char*) map_file(argv[1]);
char* b = (char*) map_file(argv[2]);
int fd = open("/proc/self/pagemap", O_RDONLY);
assert(fd>=0);
int x = a[0];
uint64_t info1 = pagemap_info(a);
int y = b[0];
uint64_t info2 = pagemap_info(b);
fprintf(stderr, "%" PRIx64 " %" PRIx64 "\n", info1, info2);
assert(info1==info2);
return 0;
}
mprotect
익명mmap
페이지:당신의 경우에는 작동하지 않지만 해결책은 메인 메모리 영역에 MAP_SHARED 파일을 사용하는 것입니다.스냅샷에서 파일은 다른 곳에 매핑되고 두 인스턴스는 모두 보호되지 않습니다.스냅샷에 매핑된 익명 페이지인 쓰기에서 데이터는 이 새 페이지에 복사되고 원본 페이지는 보호되지 않습니다.그러나 스냅샷에서 프로세스를 반복할 수 없기 때문에 이 솔루션은 사용자의 경우에는 작동하지 않습니다(일반 MAP_SHARED 영역이 아니라 일부 MAP_ANNIMING 페이지가 있는 MAP_SHARED 영역이기 때문).또한 복사본 수에 따라 확장되지 않습니다. COW 복사본이 많으면 각 복사본에 대해 동일한 프로세스를 반복해야 하며 이 페이지는 복사본에 대해 중복되지 않습니다.그리고 복사본에 있는 익명 페이지를 매핑할 수 없기 때문에 원본 영역에 있는 익명 페이지를 매핑할 수 없습니다.이 솔루션은 어떤 식으로든 작동하지 않습니다.
이것은 리눅스 커널을 건드리지 않고 이를 수행하는 유일한 방법으로 mprotect
보입니다remap_file_pages
.일반적으로 복사할 때 각 페이지에 대해 remap_file_page syscall을 만들어야 하는 단점이 있습니다. syscall을 많이 하는 것은 효율적이지 않을 수 있습니다.공유 페이지를 중복 제거할 때는 적어도 :remap_file_page a new/free 페이지를 새로 작성하거나 새 페이지를 보호 해제해야 합니다.각 페이지의 개수를 참조해야 합니다.
나는 생각하지 않습니다.mprotect()
기반 접근 방식은 매우 잘 확장될 것입니다(이와 같이 많은 메모리를 처리하는 경우).리눅스의 경우mprotect()
페이지 세분화에서는 작동합니다.vm_area_struct
confity(/tftp//tftp).mprotect()
메모리 페이지의 세분화로 인해 커널이 vm_area_message를 지속적으로 분할 및 병합됩니다.
당신은 결국 매우로 끝날 것입니다.
mm_struct
;메모리 작업의되는 vm_area_http://▁( 이 vm_area_http://▁on 에 있습니다.
O(log #vm_area_struct)
그러나 여전히 부정적인 성능 영향을 미칠 수 있습니다.이러한 구조에 대한 메모리 소비.
이러한 이유로 파일의 비선형 메모리 매핑을 수행하기 위해 remap_file_pages syscall [ http://lwn.net/Articles/24468/ ]이 생성되었습니다.이 합니다.vm_area_struct
저는 이것이 페이지 세분화 매핑을 위해 설계되었다고 생각하지 않습니다. remap_file_pages()는 페이지 당 syscall이 필요하기 때문에 이 사용 사례에 매우 최적화되지 않았습니다.
실행 가능한 유일한 해결책은 커널이 그것을 하도록 하는 것이라고 생각합니다.remap_file_pages를 사용하여 사용자 공간에서 이 작업을 수행할 수는 있지만 스냅샷에서 페이지 수에 비례하는 많은 syscall이 필요하기 때문에 상당히 비효율적일 수 있습니다.remap_file_pages의 변형이 이 작업을 수행할 수 있습니다.
그러나 이 접근 방식은 커널의 페이지 논리를 복제합니다.저는 우리가 커널이 이것을 하도록 놔두어야 한다고 생각하는 경향이 있습니다.대체로 커널에서 구현하는 것이 더 나은 해결책인 것 같습니다.커널의 이 부분을 알고 있는 사람에게는 매우 쉬운 일입니다.
KSM(커널 동일 페이지 병합):커널이 할 수 있는 것이 있습니다.페이지 중복 제거를 시도할 수 있습니다.여전히 데이터를 복사해야 하지만 커널은 데이터를 병합할 수 있어야 합니다.당신은 당신의 복사본에 대한 새로운 익명 영역을 매핑하고, memcpy로 수동으로 복사해야 합니다.madvide(start, end, MADV_MERGEABLE)
KSM을KSM을 활성화해야 합니다(루트로).
echo 1 > /sys/kernel/mm/ksm/run
echo 10000 > /sys/kernel/mm/ksm/pages_to_scan
잘 되네요, 제 작업량으로는 잘 안 되는데 결국 페이지가 많이 공유되지 않아서 그런 것 같습니다.단점은 복사를 계속해야 한다는 것입니다(효율적인 COW를 가질 수 없습니다). 그러면 커널이 페이지 병합을 해제합니다.복사를 수행할 때 페이지 및 캐시 오류가 발생합니다. KSM 데몬 스레드는 많은 CPU(전체 시뮬레이션에 대해 A00%로 실행 중인 CPU가 있음)를 사용하고 아마도 로그 캐시를 사용합니다.따라서 복사할 때는 시간이 걸리지 않지만 메모리는 얻을 수 있습니다.장기적으로 메모리를 적게 사용하고 복사본을 피하는 데 크게 신경 쓰지 않는 것이 주된 동기라면 이 솔루션이 효과적일 수 있습니다.
은 에 파일을 만들 수 ... 당신은 파일을 만들 수 있습니다./dev/shm
와 함께MAP_SHARED
을 쓴 , 다으로두다엽니다시번음다로 두 번 다시 .MAP_PRIVATE
.
언급URL : https://stackoverflow.com/questions/16965505/allocating-copy-on-write-memory-within-a-process
'programing' 카테고리의 다른 글
상태를 참조하지 않는 제네릭 돌연변이에서도 vuex가 제대로 작동할까요? (0) | 2023.06.30 |
---|---|
페이지. 사용자.신원.양식 이후에도 인증이 계속 참인증.로그아웃() (0) | 2023.06.30 |
.gitconfig에 파일을 포함할 수 있습니까? (0) | 2023.06.30 |
Git: 병합 커밋의 메시지를 편집/수정하는 방법은 무엇입니까? (0) | 2023.06.30 |
뒤에 aspx 코드를 통해 css 클래스 추가 (0) | 2023.06.30 |