2009-10-14 18 views
22

Tôi có một số mã mà tôi thường xuyên sao chép một khối bộ nhớ lớn, thường sau khi thực hiện những thay đổi rất nhỏ với nó.Tôi có thể thực hiện một memcpy copy-on-write trong Linux không?

Tôi đã triển khai một hệ thống theo dõi các thay đổi, nhưng tôi nghĩ rằng nó có thể tốt đẹp, nếu có thể nói với hệ điều hành để làm một 'copy-on-write' của bộ nhớ, và để cho nó đối phó với chỉ thực hiện một bản sao của những phần đó thay đổi. Tuy nhiên, trong khi Linux thực hiện copy-on-write, ví dụ khi fork() ing, tôi không thể tìm ra cách kiểm soát nó và tự làm nó.

+0

Bạn đang sao chép loại dữ liệu nào?Sao chép trên ghi có thể không phải là giải pháp duy nhất. –

Trả lời

16

Cơ hội tốt nhất của bạn có thể là mmap() dữ liệu gốc để lưu trữ, và sau đó mmap() cùng một tệp lần nữa bằng cách sử dụng MAP_PRIVATE.

+0

Lưu ý rằng bạn cần tạo hai ánh xạ 'MAP_PRIVATE' - ngữ nghĩa COW yêu cầu tất cả người dùng phải có bản sao COW, không có ai sử dụng bản sao" chính ". Thật không may các tập tin chính nó có vẻ là cần thiết. – caf

+0

Tại sao? Giả sử master là 'AA', và lý do cho COW là bạn muốn có một bản sao mà bạn có thể thay đổi thành' AB'. Không có lý do gì mà bản gốc 'AA' cần phải là một bản đồ riêng tư, vì không ai dự định viết cho nó. Nó chỉ là một mẫu. – MSalters

+1

Nhận xét của tôi dựa trên khả năng bản sao "gốc" cũng có thể được ghi vào, trong trường hợp đó, nó sẽ không được chỉ định nếu những thay đổi đó được phản ánh trong bản sao COW hay không. Ngoài ra, đáng tiếc là 'mmap' không cung cấp hỗ trợ vốn có cho điều này - tôi có thể chơi xung quanh với việc thêm hỗ trợ cho' mmap' để nhân đôi ánh xạ hiện có và xem cách nó đi. – caf

2

Cơ chế sao chép ghi đè được sử dụng, ví dụ: by fork() là một tính năng của MMU (Memory Management Unit), xử lý phân trang bộ nhớ cho hạt nhân. Việc truy cập MMU là một hoạt động riêng, tức là không thể được thực hiện bởi một ứng dụng không gian người dùng. Tôi cũng không biết bất kỳ API sao chép trên ghi nào cũng được xuất sang không gian người dùng.

(Sau đó, một lần nữa, tôi không chính xác một guru trên API Linux, để những người khác có thể chỉ ra các cuộc gọi API có liên quan tôi đã bỏ qua.)

Edit: Và kìa, MSalters tăng lên đến dịp này. ;-)

4

Tùy thuộc vào chính xác bạn đang sao chép, persistent data structure có thể là giải pháp cho sự cố của bạn.

2

Dễ dàng hơn khi thực hiện sao chép-ghi-ghi trong ngôn ngữ hướng đối tượng, như C++. Ví dụ, hầu hết các lớp container trong Qt là copy-on-write.

Nhưng nếu khóa học bạn có thể làm điều đó trong C quá, nó chỉ là một số công việc nhiều hơn nữa. Khi bạn muốn gán dữ liệu của bạn cho một khối dữ liệu mới, bạn không làm một bản sao, thay vào đó bạn chỉ cần sao chép một con trỏ trong một trình bao bọc xung quanh dữ liệu của bạn. Bạn cần phải theo dõi trong các khối dữ liệu của bạn về trạng thái của dữ liệu. Nếu bây giờ bạn thay đổi thứ gì đó trong khối dữ liệu mới của mình, bạn tạo bản sao "thực" và thay đổi trạng thái. Dĩ nhiên, bạn không thể sử dụng các toán tử đơn giản như "=" để gán, thay vào đó cần phải có các hàm (Trong C++ bạn chỉ cần thực hiện quá tải toán tử).

Triển khai mạnh mẽ hơn nên sử dụng bộ đếm tham chiếu thay vì cờ đơn giản, tôi để nó tùy thuộc vào bạn.

Một ví dụ nhanh chóng và dơ bẩn: Nếu bạn có một

struct big { 
//lots of data 
    int data[BIG_NUMBER]; 
} 

bạn phải thực hiện chức năng assign và getters/setters mình.

// assume you want to implent cow for a struct big of some kind 
// now instead of 
struct big a, b; 
a = b; 
a.data[12345] = 6789; 

// you need to use 
struct cow_big a,b; 
assign(&a, b); //only pointers get copied 
set_some_data(a, 12345, 6789); // now the stuff gets really copied 


//the basic implementation could look like 
struct cow_big { 
    struct big *data; 
    int needs_copy; 
} 

// shallow copy, only sets a pointer. 
void assign(struct cow_big* dst, struct cow_big src) { 
    dst->data = src.data; 
    dst->needs_copy = true; 
} 

// change some data in struct big. if it hasn't made a deep copy yet, do it here. 
void set_some_data(struct cow_big* dst, int index, int data } { 
    if (dst->needs_copy) { 
     struct big* src = dst->data; 
     dst->data = malloc(sizeof(big)); 
     *(dst->data) = src->data; // now here is the deep copy 
     dst->needs_copy = false; 
    } 
    dst->data[index] = data; 
} 

Bạn cũng cần phải viết các hàm tạo và hàm hủy. Tôi thực sự khuyên bạn nên c + + cho việc này.

+2

Điều đó không tạo ra ngữ nghĩa COW mà tôi muốn, nếu hệ điều hành đã làm nó nó sẽ chỉ sao chép (trên Mac OS X ít nhất) trang 4k đã được thay đổi, để phần còn lại của (10s hoặc 100s khác của MB) cấu trúc dữ liệu vẫn COW. Tất nhiên, tôi có thể, và có, thực hiện những gì tôi thực sự muốn, nhưng nó sẽ là tốt đẹp nếu tôi có thể có được hệ điều hành để làm điều đó cho tôi. –

+2

Phiên bản mới hơn của hạt nhân Linux có thể tự động thực hiện, hạt nhân 2.6.32+ có mã để thay thế các trang trùng lặp với các liên kết sao chép trên http://lwn.net/Articles/353501/, nhưng hệ thống con ksm là không phải là rất trưởng thành và cho đến bây giờ hoạt động theo cách khác xung quanh: Các trang được quét sau khi chúng đã được sao chép và thay thế nếu giống hệt nhau. Nếu bạn muốn nó kiểm soát nó từ không gian người dùng, bạn có thể muốn xem xét linux/mm/ksm.c và thực hiện các thay đổi bạn cần. – hirschhornsalz

+4

Giải pháp được đăng thực sự không phải là "CoW" ở tất cả, đó là phần mềm mô phỏng mà buộc tất cả các hoạt động "ghi" thông qua một lớp vô hướng. Tôi tin rằng Chris đã yêu cầu cụ thể cho một giải pháp cấp bộ nhớ bằng cách sử dụng phần cứng MMU. Và FWIW: bạn không cần một phiên bản mới của nhân Linux. BSD mmap() đã hỗ trợ MAP_PRIVATE trong nhiều thập kỷ nay - nó đã là một phần của POSIX ngay từ đầu. –

1

Bạn sẽ có thể mở bộ nhớ của riêng mình qua/proc/$ PID/mem và sau đó mmap() phần thú vị của nó với MAP_PRIVATE đến một số địa điểm khác.

+1

Điều này sẽ không hoạt động như /proc .../mem không hỗ trợ mmap. Xem thêm [tại đây] (http://stackoverflow.com/questions/5216326/mmap-on-proc-pid-mem). – coltox

0

Dưới đây là một ví dụ làm việc:

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/mman.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 

#define SIZE 4096 

int main(void) { 
    int fd = shm_open("/tmpmem", O_RDWR | O_CREAT, 0666); 
    int r = ftruncate(fd, SIZE); 
    printf("fd: %i, r: %i\n", fd, r); 
    char *buf = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, 
     MAP_SHARED, fd, 0); 
    printf("debug 0\n"); 
    buf[SIZE - 2] = 41; 
    buf[SIZE - 1] = 42; 
    printf("debug 1\n"); 

    // don't know why this is needed, or working 
    //r = mmap(buf, SIZE, PROT_READ | PROT_WRITE, 
    // MAP_FIXED, fd, 0); 
    //printf("r: %i\n", r); 

    char *buf2 = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, 
    MAP_PRIVATE, fd, 0); 
    printf("buf2: %i\n", buf2); 
    buf2[SIZE - 1] = 43; 
    buf[SIZE - 2] = 40; 
    printf("buf[-2]: %i, buf[-1]: %i, buf2[-2]: %i, buf2[-1]: %i\n", 
     buf[SIZE - 2], 
     buf[SIZE - 1], 
     buf2[SIZE - 2], 
     buf2[SIZE - 1]); 

    unlink(fd); 
    return EXIT_SUCCESS; 
} 

Tôi là một chút không chắc chắn về việc liệu tôi cần phải kích hoạt phần nhận xét ra, vì sự an toàn.

+0

Đối với tôi sự cố này trong cuộc gọi thứ hai đến mmap. Tôi muốn được quan tâm để biết nếu bạn sau đó được sử dụng mã này, hoặc một phiên bản cải tiến của nó, như tôi có một yêu cầu tương tự cho copy-on-write trong mã C? (P.S. lưu ý rằng các cuộc gọi để bỏ liên kết có vẻ sai (unlink mất một chuỗi, không phải là một bộ mô tả tập tin)). –

Các vấn đề liên quan