2011-09-21 26 views
7

Tôi muốn thay đổi kích thước vùng bộ nhớ được phân bổ bởi VirtualAlloc của cửa sổ MS. Nhìn vào tài liệu VirtualFree, có thể hủy bỏ một khu vực chỉ một phần, nhưng không thể giải phóng nó một phần. Đó là, nó có thể giải phóng một phần bộ nhớ vật lý, nhưng không phải là một phần của bộ nhớ ảo.Làm cách nào để thay đổi kích thước các vùng được VirtualAlloc phân bổ?

Tôi biết rằng có thể cần phải tái phân bổ khu vực trong trường hợp này. Tuy nhiên, việc sao chép trên toàn bộ khu vực sẽ không hiệu quả. Có cách nào để yêu cầu các cửa sổ phân bổ một khu vực mới với kích thước khác, mà chỉ vào cùng một bộ nhớ?

+0

Toàn bộ điểm của bộ nhớ ảo là trừu tượng hóa công cụ này và để thỏa thuận hạt nhân với vị trí bộ nhớ. –

+0

Ứng dụng của bạn có cần phát hành các trang của bộ nhớ ảo hay không; bạn đang chạy ra khỏi không gian địa chỉ ảo? –

+1

@DanielTrebbien: Tình hình là như sau .. Tôi cần một khu vực mới, vì vậy tôi cố gắng phân bổ nó với VirtualAlloc. Tuy nhiên, VirtualAlloc ném một bộ nhớ ngoài. Đồng thời, vì một số siêu dữ liệu tôi giữ bản thân mình, tôi biết rằng một số khu vực nhất định chỉ có một nửa. Bây giờ tôi muốn thu nhỏ hoặc phân bổ lại những khu vực đó với giá rẻ cho đến khi tôi có trí nhớ một lần nữa. – cib

Trả lời

3

Như các bạn đã đề cập, nó không xuất hiện để có thể để phần phát hành một loạt các trang dành riêng vì VirtualFree() documentation trạng thái:

Nếu tham số dwFreeType là MEM_RELEASE, [lpAddress] phải là địa chỉ cơ sở được trả về bởi hàm VirtualAlloc khi vùng của trang [đã] được đặt trước.

cũng như:

Nếu dwFreeType tham số là MEM_RELEASE, [dwSize] phải là 0 (zero).

VirtualFree() chính nó là một trình bao bọc mỏng của hàm hạt nhân NtFreeVirtualMemory(). Its documentation page (giống như ZwFreeVirtualMemory()) cũng có từ ngữ này.

Một công việc có thể là chia nhỏ một lần đặt phòng lớn với nhiều đặt phòng nhỏ hơn. Ví dụ: giả sử bạn thường đặt 8 MiB không gian địa chỉ ảo tại một thời điểm. Thay vào đó, bạn có thể cố gắng đặt trước phạm vi trong ba mươi hai đặt trước 256 KiB liền kề. Các phòng 256 KiB đầu tiên sẽ chứa một trường bit unsigned 32-bit, nơi ithứ bit được thiết lập nếu các thứ i phòng 256 KiB thu được: sản lượng

#define NOMINMAX 
#include <windows.h> 
#include <assert.h> 
#include <stddef.h> 
#include <stdint.h> 
#include <stdio.h> 
#include <stdlib.h> 

#define RESERVATION_SIZE (256*1024) 

typedef struct st_first_reservation { 
    size_t reservation_size; 
    uint32_t rfield; 
    char premaining[0]; 
} st_first_reservation; 

int main() 
{ 
    SYSTEM_INFO sys_info = { 0 }; 
    GetSystemInfo(&sys_info); 

    assert((RESERVATION_SIZE % sys_info.dwPageSize) == 0); 

    void *vp = VirtualAlloc(NULL, 32*RESERVATION_SIZE, MEM_RESERVE, PAGE_NOACCESS); 
    if (VirtualFree(vp, 0, MEM_RELEASE) == 0) { 
     fprintf(stderr, "Error: VirtualFree() failed.\n"); 
     return EXIT_FAILURE; 
    } 

    st_first_reservation *pfirst_reservation = (st_first_reservation *) VirtualAlloc(vp, RESERVATION_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 
    if (pfirst_reservation == NULL) { 
     pfirst_reservation = (st_first_reservation *) VirtualAlloc(NULL, RESERVATION_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 
     if (pfirst_reservation == NULL) { 
      fprintf(stderr, "Error: VirtualAlloc() failed.\n"); 
      return EXIT_FAILURE; 
     } 
    } 

    fprintf(stderr, "pfirst_reservation = 0x%p\n", (void *) pfirst_reservation); 

    pfirst_reservation->reservation_size = RESERVATION_SIZE; 
    pfirst_reservation->rfield = 1LU; 

    char *p = (char *) pfirst_reservation; 
    unsigned i = 1; 
    for (; i < 32; ++i) { 
     vp = VirtualAlloc(p += RESERVATION_SIZE, RESERVATION_SIZE, MEM_RESERVE, PAGE_NOACCESS); 
     if (vp != NULL) { 
      assert(((void *) vp) == p); 
      pfirst_reservation->rfield |= 1LU << i; 
      fprintf(stderr, "Obtained reservation #%u\n", i + 1); 
     } else { 
      fprintf(stderr, "Failed to obtain reservation #%u\n", i + 1); 
     } 
    } 

    fprintf(stderr, "pfirst_reservation->rfield = 0x%08x\n", pfirst_reservation->rfield); 

    return EXIT_SUCCESS; 
} 

mẫu:

 
pfirst_reservation = 0x009A0000 
Obtained reservation #2 
Obtained reservation #3 
Obtained reservation #4 
Obtained reservation #5 
Obtained reservation #6 
Obtained reservation #7 
Obtained reservation #8 
Obtained reservation #9 
Obtained reservation #10 
Obtained reservation #11 
Obtained reservation #12 
Obtained reservation #13 
Obtained reservation #14 
Obtained reservation #15 
Obtained reservation #16 
Obtained reservation #17 
Obtained reservation #18 
Obtained reservation #19 
Obtained reservation #20 
Obtained reservation #21 
Obtained reservation #22 
Obtained reservation #23 
Obtained reservation #24 
Obtained reservation #25 
Obtained reservation #26 
Obtained reservation #27 
Obtained reservation #28 
Obtained reservation #29 
Obtained reservation #30 
Obtained reservation #31 
Obtained reservation #32 
pfirst_reservation->rfield = 0xffffffff 

EDIT: tôi đã tìm thấy rằng nó là tốt hơn để "tiền dự trữ" của ba mươi hai 256 KiB dao động cùng một lúc, miễn phí, và thứ vi cố gắng dự trữ lại nhiều nhất có thể.

Tôi đã cập nhật mã và đầu ra mẫu ở trên.

Trong môi trường đa luồng, mã có thể quay trở lại phân bổ "địa điểm bất kỳ đâu" của đặt chỗ đầu tiên. Có lẽ bạn nên thử đặt trước RESERVATION_SIZE byte tại phạm vi được đặt trước sau đó giải phóng là 32*RESERVATION_SIZE byte năm lần hoặc lâu hơn, cuối cùng sẽ quay trở lại phân bổ "địa điểm bất kỳ".

+0

Thú vị, tôi đã có cùng ý tưởng đó, nhưng tôi không chắc liệu nó có hiệu quả hay không và nó sẽ được thực hiện chi tiết như thế nào. Trong thực tế, nếu tôi nghĩ về nó, một bitmap thậm chí không cần thiết, vì tôi sẽ chỉ thu hẹp hoặc phát triển "khu vực". Vì vậy, một truy cập của các trang hiện đang được phân bổ là đủ. Dù sao, cảm ơn rất nhiều cho mã, quá xấu tôi không thể bỏ phiếu nhiều hơn một lần. – cib

+0

@cib: Tôi không chắc liệu bạn có thể nhận được mà không có bitmap. Nó có thể xảy ra, ví dụ, đặt phòng 32 không thể có được. Thậm chí nếu bạn bảo vệ tất cả các cuộc gọi đến 'VirtualAlloc()' với một mutex, có thể cho một tiến trình khác để phân bổ không gian địa chỉ ảo trong không gian địa chỉ của quá trình của bạn thông qua ['VirtualAllocEx()'] (http://msdn.microsoft. com/en-us/library/aa366890.aspx). –

+0

Tôi nghĩ rằng một khi tôi không thể cung cấp một đoạn bộ nhớ liền kề, tôi phải phân bổ lại hoặc gây ra lỗi. Nếu "vùng" của tôi có lỗ hổng trong nó, tôi không thể sử dụng nó như một đoạn bộ nhớ duy nhất, mà đúng hơn là phải coi nó như là một bộ sưu tập các trang riêng biệt. Ngoài ra, trong khi điều này là một chút off-topic, bạn có thể giải thích lý do tại sao các quá trình khác có thể muốn phân bổ bộ nhớ trong quá trình của tôi? Nghe có vẻ như một cái gì đó mà sẽ không được an toàn anyway, mà không có một hệ thống cho phép liên quá trình thích hợp. – cib

2

Không một câu trả lời, nhưng tôi phải hỏi:

Xét đau bạn đang ở, các hits thực hiện VirtualAlloc(), và không di động của mã của bạn; so với bất kỳ giá trị nào mà VIrtualAlloc() đưa ra, bạn có thể cân nhắc sử dụng malloc() và bạn bè thay thế không? IOW, VirtualAlloc() có mang lại lợi ích thực sự nào không?

Theo ý kiến ​​của tôi (có thể là chỉ ý kiến ​​của tôi), sức mạnh và tính tổng quát của malloc() lớn hơn bất kỳ sức hấp dẫn nào mà VirtualAlloc() hứa hẹn. Và nó sẽ cho phép bạn đối phó với các khu vực của bạn đơn giản hơn nhiều.

Xin lỗi vì không trả lời. Tôi ghét nó khi mọi người hỏi "ai là bao giờ thậm chí sẽ nghĩ để làm rằng?" Nhưng tất nhiên đó là tất cả khác nhau khi Tôi một trong những hỏi "tại sao" :-)

+0

Đơn giản, tôi đang viết trình quản lý bộ nhớ cấp malloc của riêng tôi vì lý do malloc không hiệu quả với những gì tôi đang làm_ – cib

+0

@cib, trong hầu hết các trường hợp, ngay cả khi sử dụng trực tiếp malloc là không hiệu quả, gián tiếp sử dụng malloc (ví dụ: malloc để có được một khối lớn mà bạn tiếp tục chia nhỏ) thường là ok ... – bdonlan

+0

@bdonlan: Đây có thể là trường hợp, nhưng nó có vẻ giống như một hack tốt nhất. Sử dụng malloc, bạn có ít quyền kiểm soát đối với vị trí và cách thức bạn phân bổ. Ví dụ: một số yêu cầu của tôi có thể sẽ bao gồm việc căn chỉnh trang và địa chỉ trong một dải địa chỉ nhất định. Chắc chắn, một số triển khai malloc có thể làm việc ra, nhưng kể từ khi malloc là trừu tượng, bạn không bao giờ có thể chắc chắn. – cib

1

Nếu bạn muốn giảm bớt một phân bổ, bạn có thể sử dụng VirtualFree với MEM_DECOMMIT trên subrange của việc phân bổ. Lưu ý rằng điều này sẽ không giải phóng không gian địa chỉ ; chỉ RAM vật lý. Nếu bạn muốn phát triển nó, bạn có thể thử VirtualAlloc chuyển địa chỉ ngay sau khi phân bổ hiện tại của bạn. Điều này có thể, tất nhiên, thất bại, tại thời điểm đó bạn cần phải sao chép bộ nhớ.

Bạn cũng có thể thử sử dụng GlobalAlloc với GMEM_MOVEABLEGlobalReAlloc (hoặc các hàm Heap * tương đương).

Nếu bạn cần giải phóng không gian địa chỉ, bạn có thể thử sử dụng các đối tượng ánh xạ bộ nhớ ẩn danh và thay đổi thời gian ánh xạ của chúng tại thời gian chạy - hoặc chỉ cần sử dụng 64 bit để có thêm không gian địa chỉ.

+0

Không giúp được gì, anh ấy đã hết dung lượng.VirtualAlloc/Free không liên quan gì đến RAM trên hệ điều hành bộ nhớ ảo được phân trang theo yêu cầu. –

+0

@HansPassant, xem gợi ý cuối cùng của tôi - anh ấy có thể sử dụng các đối tượng ánh xạ bộ nhớ ẩn danh và các cửa sổ bản đồ trong số chúng. Hoặc chỉ cần truy cập 64 bit. – bdonlan

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