Come hai detto, non risulta essere possibile rilasciare parzialmente un intervallo di pagine riservate, perché le VirtualFree()
documentation stati:
Se il parametro dwFreeType è MEM_RELEASE, [lpAddress] deve essere l'indirizzo di base restituito dalla funzione VirtualAlloc quando l'area delle pagine [è stata] riservata.
nonché:
Se il parametro dwFreeType è MEM_RELEASE, [dwSize] deve essere 0 (zero).
VirtualFree()
è esso stesso un involucro sottile della funzione kernel NtFreeVirtualMemory()
. Its documentation page (lo stesso di ZwFreeVirtualMemory()
) ha questa dicitura.
Una possibile soluzione è quella di suddividere una singola, grande prenotazione con più piccole. Ad esempio, si supponga di riservare normalmente 8 MiB di spazio di indirizzi virtuali alla volta. Si potrebbe invece tentare di riservare l'intervallo in trentadue prenotazioni contigue 256 KiB. Il primo 256 KiB prenotazione conterrebbe un campo di bit senza segno a 32-bit, dove il iesimo bit viene impostato se il iesimo 256 KiB prenotazione è stata ottenuta: uscita
#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;
}
Esempio:
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: ho scoperto che è molto meglio di "pre-riserva" il trentadue 256 KiB va tutto in una volta, libero e th prova a riscriverne il maggior numero possibile.
Ho aggiornato il codice e l'output di esempio sopra.
In un ambiente con multithreading, il codice può ricadere sull'allocazione "luogo dovunque" della prima prenotazione. Forse è una buona idea tentare di riservare i byte RESERVATION_SIZE
in un intervallo riservato, quindi liberato, di 32*RESERVATION_SIZE
byte cinque o più volte, ricadendo infine nell'allocazione "luogo dovunque".
L'intero punto della memoria virtuale è di astrarre questa roba e lasciare che il kernel gestisca il posizionamento della memoria. –
La tua applicazione ha bisogno di rilasciare pagine di memoria virtuale; stai esaurendo lo spazio degli indirizzi virtuali? –
@DanielTrebbien: La situazione è la seguente .. Ho bisogno di una nuova regione, quindi provo ad allocarla con VirtualAlloc. Tuttavia, VirtualAlloc genera una memoria esaurita. Allo stesso tempo, a causa di alcuni metadati che sto mantenendo, so che alcune regioni sono solo mezzo pieno. Mi piacerebbe ora restringere o ridimensionare quelle regioni a poco prezzo finché non avrò di nuovo memoria. – cib