2011-09-21 17 views
7

Me gustaría cambiar el tamaño de una región de memoria asignada por VirtualAlloc de MS window. Al mirar la documentación de VirtualFree, es posible liberar una región solo en parte, pero no es posible liberarla parcialmente. Es decir, es posible liberar parte de la memoria física, pero no parte de la memoria virtual.¿Cómo cambiar el tamaño de las regiones asignadas por VirtualAlloc?

Soy consciente de que puede ser necesario reasignar la región en tal caso. Sin embargo, copiar en toda la región sería bastante ineficiente. ¿Hay alguna manera de pedirle a Windows que asigne una nueva región con un tamaño diferente que apunta a la misma memoria?

+0

El objetivo de la memoria virtual es abstraer todo esto y dejar que el kernel se ocupe de la ubicación de la memoria. –

+0

¿Necesita su aplicación liberar páginas de memoria virtual? ¿Se está quedando sin espacio de direcciones virtuales? –

+1

@DanielTrebbien: La situación es la siguiente ... Necesito una nueva región, así que intento asignarla a VirtualAlloc. Sin embargo, VirtualAlloc arroja una falta de memoria. Al mismo tiempo, debido a algunos metadatos que me mantengo, sé que ciertas regiones están medio llenas. Ahora me gustaría reducir o reasignar esas regiones a bajo costo hasta que vuelva a tener memoria. – cib

Respuesta

3

Como se ha mencionado, no parece ser posible liberar parcialmente un rango de páginas reservadas porque los VirtualFree() documentation estados:

Si el parámetro dwFreeType es MEM_RELEASE, [lpAddress] debe ser la dirección base devuelta por la función VirtualAlloc cuando la región de páginas [fue] reservada.

así como:

Si el parámetro dwFreeType es MEM_RELEASE, [dwSize] debe ser 0 (cero).

VirtualFree() es en sí mismo una envoltura delgada de la función de kernel NtFreeVirtualMemory(). Its documentation page (lo mismo que para ZwFreeVirtualMemory()) también tiene esta fraseología.

Una solución posible es dividir una única reserva grande con varias más pequeñas. Por ejemplo, supongamos que normalmente reserva 8 MiB de espacio de direcciones virtuales a la vez. En cambio, podría intentar reservar el rango en treinta y dos reservas contiguas de 256 KiB. La primera reserva 256 KiB contendría un campo de bits sin signo de 32 bits, donde el iº bit se establece si el iº se obtuvo reserva 256 KiB: salida

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

muestra:

 
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: he descubierto que es mucho mejor "pre-reserva" los treinta y dos 256 KiB oscila todos a la vez, gratis, y TH Trate de volver a reservar tantas como pueda.

He actualizado el código y la salida de muestra anterior.

En un entorno multiproceso, el código puede volver a la asignación de "lugar en cualquier lugar" de la primera reserva. Tal vez es una buena idea intentar reservar RESERVATION_SIZE bytes en un rango reservado-luego-liberado de 32*RESERVATION_SIZE bytes cinco o más veces, volviendo finalmente a la asignación de "colocar en cualquier lugar".

+0

Interesante, tuve la misma idea, pero no estaba seguro de si sería eficiente o cómo se haría en detalle. De hecho, si lo pienso, un mapa de bits ni siquiera es necesario, ya que solo reduciré o aumentaré la "región". Por lo tanto, un contador de páginas asignadas actualmente debería ser suficiente. De todos modos, muchas gracias por el código, lástima que no puedo votar más de una vez. – cib

+0

@cib: no estoy seguro de si puede salir adelante sin el mapa de bits. Puede suceder, por ejemplo, que no se pudo obtener la reserva número 32. Incluso si protege todas las llamadas a 'VirtualAlloc()' con un mutex, es posible que otro proceso asigne espacio de direcciones virtuales en el espacio de direcciones de su proceso a través de ['VirtualAllocEx()'] (http://msdn.microsoft. com/en-us/library/aa366890.aspx). –

+0

Creo que una vez que no puedo proporcionar un trozo contiguo de memoria, tengo que reasignar o generar un error. Si mi "región" tiene agujeros, no puedo usarla como un solo trozo de memoria, sino que debo tratarla como lo que es, una colección de páginas separadas. Además, aunque esto está un poco fuera de tema, ¿puede explicar por qué otros procesos pueden querer asignar memoria en mi proceso? Suena como algo que no será seguro de todos modos, sin un sistema de permisos entre procesos adecuado. – cib

2
No

una respuesta, pero tengo que preguntar:

Teniendo en cuenta el dolor que se encuentre, los éxitos de rendimiento de VirtualAlloc(), y la no transferibilidad de su código; frente a cualquier valor que da VrtualAlloc(), ¿podría considerar usar malloc() y amigos en su lugar? IOW, ¿otorga VirtualAlloc() alguna ventaja real?

En mi opinión (tal vez solo mi opinión), el poder y la generalidad de malloc() superan a cualquiera de los atractivos que VirtualAlloc() promete. Y le permitiría tratar con sus regiones mucho más directamente.

Lo siento por la falta de respuesta. Odio cuando la gente pregunta "¿quién nunca habría siquiera pensar hacer que?" Pero, por supuesto, todo es diferente cuando estoy el preguntar "por qué" :-)

+0

Simple, estoy escribiendo mi propio administrador de memoria de nivel malloc _porque malloc no es eficiente para lo que estoy haciendo_ – cib

+0

@cib, en la mayoría de los casos, incluso si el uso directo de malloc es ineficiente, uso indirecto de malloc (es decir, malloc para obtener un bloque grande que usted subdivide) generalmente está bien ... – bdonlan

+0

@bdonlan: Este puede ser el caso, pero suena como un truco en el mejor de los casos. Al usar malloc, tienes poco control sobre dónde y cómo lo asignas. Por ejemplo, algunos de mis requisitos probablemente incluirán alineación de página y direcciones en un cierto rango de direcciones. Claro, algunas implementaciones malloc pueden funcionar, pero como malloc es abstracto, nunca se puede estar seguro. – cib

1

Si desea reducir una asignación, puede utilizar VirtualFree con MEM_DECOMMIT en un subrango de la asignación. Tenga en cuenta que esto no liberará espacio de direcciones; solo RAM física Si desea crecer, puede intentar VirtualAlloc pasando una dirección inmediatamente después de su asignación existente. Esto puede, por supuesto, fallar, en ese momento debe copiar la memoria.

También puede intentar usar GlobalAlloc con GMEM_MOVEABLE y GlobalReAlloc (o las funciones Heap * equivalentes).

Si necesita liberar espacio de direcciones, puede intentar usar objetos de mapeo de memoria anónimos y cambiar su ventana mapeada en tiempo de ejecución, o simplemente usar 64 bits para obtener espacio de direcciones adicional.

+0

No ayuda, está fuera del espacio de direcciones.VirtualAlloc/Free no tiene nada que ver con la RAM en un sistema operativo de memoria virtual con paginación demandada. –

+0

@HansPassant, vea mi última sugerencia: puede usar objetos anónimos de mapeo de memoria, y mapear ventanas de ellos. O simplemente ve a 64 bits. – bdonlan

Cuestiones relacionadas