Как получить copy-on-write для работы с общей памятью в linux
Я попытался написать небольшое приложение, чтобы ознакомиться с концепцией копирования на запись в пользовательском пространстве. Я прочитал ответ MSalters и понял, что это сработает, только если я начну с mmap
' ed файл для хранения моих данных. Поскольку мне не нужна настойчивость на основе файлов, я попытался сделать то же самое с общей памятью. Сначала Я!--2--> ' ed и инициализировал shm fd, затем я сопоставил вторую копию с MAP_PRIVATE
и прочитайте еще раз. Тем не менее, просто чтение из него заставляет ядро копировать все это, занимая значительно больше времени и съедая вдвое больше памяти. Почему это не делает корова?
вот программа, которую я придумал, чтобы проиллюстрировать поведение:
#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
.
массив, который он создает, содержит 4 ГБ целых значений. Прямо перед завершением программы, однако, выделяет 8 ГБ общей памяти и в /proc/<pid>/smaps
вы можете видеть, что он фактически сделал полную копию во время только для чтения операция. Я понятия не имею, почему он это делает. Это ошибка ядра? Или я что-то упускаю?
Спасибо за любые идеи. Ларс!--11-->
редактировать
Вот соответствующее содержание /proc/<pid>/smaps
на Ubuntu 14.04 (3.13.0-24):
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
1 ответов
копирования не было. В файле smaps есть подсказка:
Size: 4194304 kB
Rss: 4194304 kB
Pss: 2097152 kB
см. как Pss-это половина реального размера отображаемой области? Это потому, что он разделен на два использования (Pss = пропорциональный общий размер). То есть один и тот же файл дважды сопоставляется с различными диапазонами виртуальной памяти, но базовые физические страницы одинаковы для обоих сопоставлений.
чтобы выяснить физические адреса соответствующих страниц вы можете использовать инструмент здесь. Сохранить как page-types.c
, run make page-types
а то ./page-types -p <pid> -l -N
. Вы увидите, что разные виртуальные адреса (в первом столбце) сопоставляются с теми же физическими страницами (во втором столбце).
если добавить PROT_WRITE
бит разрешения для второго отображения и вызов init(Acopy)
, вы увидите, что Pss переходит на 4GB, а физические адреса соответствующих страниц больше не совпадают.
TL; DR COW работает.