我如何获得副本
我试图编写一个小型应用程序来熟悉用户空间中写入时复制的概念。 我已经阅读了MSalters的答案,并认为只有在开始使用mmap
的ed文件来存储数据时,它才会起作用。因为我不需要基于文件的持久性,所以我尝试使用shared记忆。 首先,我mmap
“ED和初始化的SHM FD,然后我用映射的第二个副本MAP_PRIVATE
并从中再次读取。 然而,从内核中读取会导致内核复制整个内容,花费相当多的时间并耗尽内存的两倍。 为什么它没有COW?
下面是我提出的程序来说明这种行为:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <assert.h>
static const size_t ARRAYSIZE = 1UL<<30;
void init(int* A)
{
for (size_t i = 0; i < ARRAYSIZE; ++i)
A[i] = i;
}
size_t agg(const int* A)
{
size_t sum = 0;
for (size_t i = 0; i < ARRAYSIZE; ++i)
sum += A[i];
return sum;
}
int main()
{
assert(sizeof(int) == 4);
shm_unlink("/cowtest");
printf("ARRAYSIZE: %lun", ARRAYSIZE);
int fd = shm_open("/cowtest", O_RDWR | O_CREAT | O_TRUNC, 0);
if (fd == -1)
{
perror("Error allocating fdn");
return 1;
}
if (ftruncate(fd, sizeof(int) * ARRAYSIZE) == -1)
{
perror("Error ftruncaten");
return 1;
}
/* Open shm */
int* A= (int*)mmap(NULL, sizeof(int) * ARRAYSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (A == (int*)-1)
{
perror("Error mapping A to memoryn");
return 1;
}
init(A);
/* Create cow copy */
int* Acopy = (int*)mmap(NULL, sizeof(int) * ARRAYSIZE, PROT_READ, MAP_PRIVATE, fd, 0);
if (Acopy == (int*)-1)
{
printf("Error mapping copy from filen");
return 1;
}
/* Aggregate over A */
size_t sumA = agg(A);
size_t expected = (ARRAYSIZE * (ARRAYSIZE - 1)) >> 1;
assert(expected == sumA);
/* Aggregate over Acopy */
size_t sumCopy = agg(Acopy);
assert(expected == sumCopy);
shm_unlink("/cowtest");
printf("Enter to exitn");
getchar();
return 0;
}
我用g++ -O3 -mtune=native -march=native -o shm-min shm-min.cpp -lrt
编译它g++ -O3 -mtune=native -march=native -o shm-min shm-min.cpp -lrt
。
它创建的数组包含4GB的整数值。 然而,在终止程序之前分配8GB的共享内存,而在/proc/<pid>/smaps
您可以看到它在只读操作期间实际执行了完整的拷贝。 我不知道它为什么这样做。 这是一个内核错误? 或者我错过了什么?
非常感谢任何见解。 拉尔斯
编辑这里是Ubuntu 14.04(3.13.0-24)中/proc/<pid>/smaps
的相关内容:
7f3b9b4ae000-7f3c9b4ae000 r--p 00000000 00:14 168154 /run/shm/cowtest (deleted)
Size: 4194304 kB
Rss: 4194304 kB
Pss: 2097152 kB
Shared_Clean: 0 kB
Shared_Dirty: 4194304 kB
Private_Clean: 0 kB
Private_Dirty: 0 kB
Referenced: 4194304 kB
Anonymous: 0 kB
AnonHugePages: 0 kB
Swap: 0 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Locked: 0 kB
VmFlags: rd mr mw me sd
7f3c9b4ae000-7f3d9b4ae000 rw-s 00000000 00:14 168154 /run/shm/cowtest (deleted)
Size: 4194304 kB
Rss: 4194304 kB
Pss: 2097152 kB
Shared_Clean: 0 kB
Shared_Dirty: 4194304 kB
Private_Clean: 0 kB
Private_Dirty: 0 kB
Referenced: 4194304 kB
Anonymous: 0 kB
AnonHugePages: 0 kB
Swap: 0 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Locked: 0 kB
VmFlags: rd wr sh mr mw me ms sd
没有复制。 smaps文件有一个提示:
Size: 4194304 kB
Rss: 4194304 kB
Pss: 2097152 kB
看看Pss如何是映射区域实际大小的一半? 这是因为它被两种用法划分(Pss =比例共享大小)。 也就是说,您将相同的文件映射两次到不同范围的虚拟内存,但两个映射的底层物理页面是相同的。
要找出相关页面的物理地址,你可以在这里使用一个工具。 将其另存为page-types.c
,运行make page-types
,然后运行./page-types -p <pid> -l -N
。 您将看到不同的虚拟地址(在第一列中)映射到相同的物理页面(在第二列中)。
如果为第二个映射添加PROT_WRITE
权限位并调用init(Acopy)
,您将看到Pss跳转到4GB,并且相应页面的物理地址不再相同。
TL; DR COW工作。
链接地址: http://www.djcxy.com/p/80129.html上一篇: How do I get copy