How do I get copy

I tried to write a small application to get familiar with the concept of copy-on-write in user space. I've read through the answer by MSalters and figured that it would only work if I started with a mmap 'ed file to store my data in. As I don't need file based persistency, I tried to do the same thing with shared memory. First I mmap 'ed and initialized a shm fd, then I mapped a second copy with MAP_PRIVATE and read from it again. However, just reading from it causes the kernel to copy the whole thing, taking considerably more time and eating up twice the memory. Why does it not do COW?

Here's the program I came up with to illustrate the behavior:

#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;
}

I compiled it with g++ -O3 -mtune=native -march=native -o shm-min shm-min.cpp -lrt .

The array it creates contains 4GB of integer values. Right before terminating the program however allocates 8GB of shared memory, and in /proc/<pid>/smaps you can see that it actually did a full copy during the read only operation. I have no idea why it does that. Is this a kernel bug? Or am I missing something?

Thanks a lot for any insights. Lars

Edit Here's the relevant content of /proc/<pid>/smaps on 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

There was no copying. The smaps file has a hint:

Size:            4194304 kB
Rss:             4194304 kB
Pss:             2097152 kB

See how Pss is half the real size of the mapped area? That's because it is divided by two usages (Pss = proportional shared size). That is, you have the same file mapped twice to different ranges of virtual memory, but the underlying physical pages are the same for both mappings.

To figure out physical addresses of the relevant pages you can use a tool here. Save it as page-types.c , run make page-types and then ./page-types -p <pid> -l -N . You will see that different virtual addresses (in the first column) map to the same physical pages (in the second column).

If you add PROT_WRITE permission bit for the second mapping, and call init(Acopy) , you will see that Pss jumps to 4GB, and the physical addresses of the corresponding pages are no longer the same.

TL;DR COW works.

链接地址: http://www.djcxy.com/p/80130.html

上一篇: 无论间隔值PYTHON如何,percent(interval = None)始终返回0

下一篇: 我如何获得副本