2011-09-21 22 views
7

Vorrei ridimensionare una regione di memoria allocata da VirtualAlloc della finestra MS. Guardando la documentazione di VirtualFree, è possibile disimpegnare una regione solo parzialmente, ma non è possibile rilasciarla parzialmente. Cioè, è possibile rilasciare parte della memoria fisica, ma non parte della memoria virtuale.Come ridimensionare le regioni allocate da VirtualAlloc?

Sono consapevole che potrebbe essere necessario riassegnare la regione in questo caso. Tuttavia, copiare su tutta la regione sarebbe piuttosto inefficiente. C'è un modo per chiedere a Windows di allocare una nuova regione con una dimensione diversa, che punti alla stessa memoria?

+0

L'intero punto della memoria virtuale è di astrarre questa roba e lasciare che il kernel gestisca il posizionamento della memoria. –

+0

La tua applicazione ha bisogno di rilasciare pagine di memoria virtuale; stai esaurendo lo spazio degli indirizzi virtuali? –

+1

@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

risposta

3

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".

+0

Interessante, ho avuto la stessa idea, ma non ero sicuro se sarebbe stato efficiente o come sarebbe stato fatto in dettaglio. In effetti, se ci penso, una bitmap non è nemmeno necessaria, dal momento che ridurrò o aumenterò la "regione". Pertanto, un contatore di pagine attualmente assegnate dovrebbe essere sufficiente. Comunque, grazie mille per il codice, peccato non posso votare più di una volta. – cib

+0

@cib: Non sono sicuro che tu possa farcela senza la bitmap. Ad esempio, può accadere che non sia possibile ottenere la 32a prenotazione. Anche se proteggi tutte le chiamate a 'VirtualAlloc()' con un mutex, è possibile che un altro processo allochi lo spazio degli indirizzi virtuali nello spazio degli indirizzi del tuo processo tramite ['VirtualAllocEx()'] (http://msdn.microsoft. com/it-it/library/aa366890.aspx). –

+0

Penso che una volta che non sono in grado di fornire una parte contigua della memoria, devo riallocare o generare un errore. Se la mia "regione" ha dei buchi, non posso usarla come un singolo pezzo di memoria, ma piuttosto trattarla come ciò che è, una raccolta di pagine separate. Inoltre, mentre questo è un po 'fuori tema, puoi spiegare perché altri processi potrebbero voler allocare memoria nel mio processo? Sembra qualcosa che non sarà sicuro in ogni caso, senza un adeguato sistema di autorizzazione tra processi. – cib

2

Non una risposta, ma devo chiedere:

Considerando il dolore si trovi, i colpi di prestazioni di VirtualAlloc(), e la non portabilità del codice; come contro qualsiasi valore fornito da VIrtualAlloc(), potresti prendere in considerazione l'idea di usare malloc() e gli amici invece? IOW, VirtualAlloc() conferisce alcun vantaggio reale?

A mio parere (forse solo mia opinione), il potere e la generalità di malloc() superano il fascino che VirtualAlloc() promette. E ti permetterebbe di trattare con le tue regioni molto più direttamente.

Siamo spiacenti per la mancata risposta. Odio quando la gente chiede "che mai sarebbe nemmeno pensare fare che?" Ma, naturalmente, è tutto diverso quando Sono quello che chiede "perché" :-)

+0

Semplice, sto scrivendo il mio gestore memoria di livello malloc _così perché malloc non è efficiente per quello che sto facendo_ – cib

+0

@cib, nella maggior parte dei casi, anche se l'uso diretto di malloc è inefficiente, uso indiretto di malloc (es. malloc per ottenere un grande blocco che tu dividi ulteriormente) di solito è ok ... – bdonlan

+0

@bdonlan: potrebbe essere il caso, ma suona come un hack al meglio. Usando malloc, hai poco controllo su dove e come assegni. Ad esempio, alcuni dei miei requisiti includeranno probabilmente allineamento della pagina e indirizzi in un determinato intervallo di indirizzi. Certo, alcune implementazioni di malloc potrebbero funzionare, ma dal momento che malloc è astratto, non si può mai essere sicuri. – cib

1

Se si desidera ridurre un'allocazione, è possibile utilizzare VirtualFree con MEM_DECOMMIT su un sottoinsieme della ripartizione. Si noti che ciò non libererà lo spazio indirizzo ; solo RAM fisica. Se vuoi farlo crescere, puoi provare VirtualAlloc passando un indirizzo immediatamente dopo la tua allocazione esistente. Questo può, naturalmente, fallire, a quel punto è necessario copiare la memoria.

È inoltre possibile provare a utilizzare GlobalAlloc con GMEM_MOVEABLE e GlobalReAlloc (o le equivalenti funzioni di heap *).

Se è necessario liberare spazio indirizzo, è possibile provare a utilizzare oggetti anonimi di mappatura della memoria e modificare la finestra mappata in fase di esecuzione oppure utilizzare semplicemente 64 bit per ottenere ulteriore spazio di indirizzamento.

+0

Non aiuta, è fuori dallo spazio degli indirizzi.VirtualAlloc/Free non ha nulla a che fare con la RAM su un sistema operativo di memoria virtuale a richiesta della pagina. –

+0

@HansPassant, vedere il mio ultimo suggerimento: può utilizzare oggetti anonimi di mappatura della memoria e mappare le finestre al di fuori di essi. O vai a 64-bit. – bdonlan

Problemi correlati