Как изменить размеры областей, выделенных VirtualAlloc? - PullRequest
7 голосов
/ 21 сентября 2011

Я хотел бы изменить размер области памяти, выделенной в окне MSA VirtualAlloc. Глядя на документацию VirtualFree, можно разделить регион только частично, но невозможно частично его освободить. То есть можно освободить часть физической памяти, но не часть виртуальной памяти.

Я знаю, что в таком случае может потребоваться перераспределение региона. Однако копирование по всему региону будет довольно неэффективным. Есть ли способ попросить окна выделить новую область другого размера, которая указывает на ту же память?

Ответы [ 3 ]

4 голосов
/ 22 сентября 2011

Как вы упомянули, представляется невозможным частично освободить диапазон зарезервированных страниц, поскольку документация VirtualFree() гласит:

Если параметр dwFreeType имеет значение MEM_RELEASE, [ lpAddress ] должен быть базовым адресом, возвращаемым функцией VirtualAlloc , когда регион страниц [было] зарезервировано.

а также:

Если параметр dwFreeType имеет значение MEM_RELEASE, [ dwSize ] должно быть 0 (ноль).

VirtualFree() сам по себе является тонкой оболочкой функции ядра NtFreeVirtualMemory(). На странице документации (такой же, как для ZwFreeVirtualMemory()) также есть эта формулировка.

Один из возможных обходных путей - разделить одно большое резервирование с несколькими меньшими. Например, предположим, что вы обычно резервируете 8 МБ виртуального адресного пространства одновременно. Вместо этого вы можете попытаться зарезервировать диапазон в тридцати двух смежных резервациях по 256 КиБ. Первое резервирование 256 КиБ будет содержать 32-разрядное поле без знака, где бит i th устанавливается, если i th Зарезервировано 256 КиБ:

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

Пример вывода:

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

РЕДАКТИРОВАТЬ: Я обнаружил, что гораздо лучше "предварительно зарезервировать" тридцать два 256-килобайтных диапазона одновременно, бесплатно, а затем попытаться зарезервировать как можно больше .

Я обновил код и пример вывода выше.

В многопоточной среде код может возвращаться к распределению «место где угодно» первого резервирования. Возможно, это хорошая идея - попытаться зарезервировать RESERVATION_SIZE байт в зарезервированном-то-освобожденном диапазоне 32*RESERVATION_SIZE байт пять или более раз, в конце концов вернувшись к распределению «где угодно».

1 голос
/ 21 сентября 2011

Если вы хотите сократить выделение, вы можете использовать VirtualFree с MEM_DECOMMIT в поддиапазоне выделения. Обратите внимание, что это не освободит адресное пространство ; только физическая оперативная память. Если вы хотите увеличить его, вы можете попробовать VirtualAlloc передать адрес сразу после вашего существующего выделения. Это может, конечно, потерпеть неудачу, в этот момент вам нужно скопировать память.

Вы также можете попробовать использовать GlobalAlloc с GMEM_MOVEABLE и GlobalReAlloc (или эквивалентными функциями Heap *).

Если вам нужно освободить адресное пространство, вы можете попробовать использовать анонимные объекты отображения памяти и изменить их отображенное окно во время выполнения - или просто использовать 64-разрядную версию для получения дополнительного адресного пространства.

1 голос
/ 21 сентября 2011

Не ответ, но я должен спросить:

Учитывая боль, которую вы испытываете, снижение производительности VirtualAlloc () и непереносимость вашего кода; В отличие от любого значения, которое дает VIrtualAlloc (), не могли бы вы рассмотреть возможность использования вместо него malloc () и друзей? IOW, дает ли VirtualAlloc () какое-либо реальное преимущество?

По моему мнению (может быть только мое мнение), мощность и универсальность malloc () перевешивают любые привлекательности, которые обещает VirtualAlloc (). И это позволит вам гораздо проще справляться со своими регионами.

Извините за отсутствие ответа. Я ненавижу, когда люди спрашивают, "кто когда-либо будет даже думать , чтобы сделать , что ?" Но, конечно, все по-другому, когда Я тот, кто спрашивает «почему» :-)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...