(Win32 : WindowsProgramming-23) Virtual Memory Allocation

Posted by : at

Category : win32   WindowsProgramming


가상 메모리 사용


   |-----------------------|
   |                       |
   | Kernel Mode Partition |
   |                       |
   |-----------------------|
   |                       |
   |     64Kb 접근 금지    |
   |                       |
   |-----------------------|  <- 0x7FFF 0000
   |                       |
   |    다양한 DLL         |
   |                       |
   |                       |  <== 실제로 어플리케이션이 사용가능한 공간
   |                       |
   |      Heap, Stack      |
   |                       |
   |         ...           |
   |-----------------------|  <- 0x0001 0000
   |                       |
   |  NULL Pointer 파티션  |
   |                       |
   |-----------------------|

  • 가상 주소공간을 할당하려면 VirtualAlloc()을 사용하면 된다.
  • 가상 주소공간 사용을 이해하려면 예약(RESERVE), 확정(COMMIT)을 먼저 알아야 한다.

  • 예약(RESERVE)
    • 실제 물리 공간에 메모리를 할당하지 않고 Process의 Virtual Address에 주소 공간만 확보
    • 예약된 메모리는 아직 사용할 수 없다.
  • 확정(COMMIT)
    • 예약된 가상 주소를 물리 저장소와 연결하는 작업
    • 예약된 주소의 일부분만 확정할 수도 있다.

#include <stdio.h>
#include <Windows.h>

int main()
{
    char* p1 = (char*)VirtualAlloc(
                    (void*)0x56781234,
                    1024*1024*10,
                    MEM_RESERVE,    // 주소만 할당
                    PAGE_READWRITE  // 읽고 쓰기 가능하게
                    );

    printf("Reserved Addr : %p\n", p1); // 0x56780000
}
LPVOID VirtualAlloc(
  LPVOID lpAddress,             // 예약을 원하는 가상주소, 할당단위는 64kb의 배수
  SIZE_T dwSize,                // 예약을 원하는 가상주소의 크기 PAGE 크기의 배수
  DWORD  flAllocationType,      // MEM_RESERVE, MEM_COMMIT, MEM_TOP_DOWN(웬만하면 높은 주소부터 메모리를 할당해 달라)
  DWORD  flProtect              // 보호속성, PAGE_NOACCESS ...
);
#include <stdio.h>
#include <Windows.h>

int main()
{
    char* p1 = (char*)VirtualAlloc(
                    (void*)0x56781234,
                    1024*1024*100,  // 100 mb를 잡아 버린다면?
                    MEM_RESERVE,    
                    PAGE_READWRITE  
                    );

    printf("Reserved Addr : %p\n", p1); // 0 -> 할당불가, 겹치는 부분이 있어서 그럼
    // 이렇게 메모리를 크게 할당할때는 차라리 원하는 메모리 공간이 없다(0)으로 하는게 나음.

    *p1 = 'A';  // Error - 아직 물리주소에 할당이 안되어 접근불가
}
#include <stdio.h>
#include <Windows.h>

int main()
{
    char* p1 = (char*)VirtualAlloc(
                    (void*)0,
                    1024*1024*100,
                    MEM_RESERVE | MEM_TOP_DOWN,
                    PAGE_READWRITE);

    printf("Reserved Addr : %p\n", p1);

    char* p2 = (char*)VirtualAlloc(
                p1 + 4096,       // p1의 시작주소에서 1 PAGE 떨어진 곳에
                4096,                   // 1 PAGE만 할당
                MEM_COMMIT,             // 확정해 달라
                PAGE_READWRITE);

    printf("Reserved Addr : %p\n", p2);

    *(p1 + 4096) = 'A';  // Okay!

    // 메모리 해지
    VirtualFree(p2, 4096, MEM_DECOMMIT);    // 이건 필수는 아님
    VirtualFree(p1, 0, MEM_RELEASE);        // 이거만 해도 MEM_DECOMMIT까지 자동 호출됨.
}

#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <stdio.h>

typedef struct _CELL
{
    char data[4091];        // 4k - 5 (4k보다 작다)
} CELL;

int main()
{
    CELL* pCell = (CELL*)VirtualAlloc(0, 
                        sizeof(CELL) * 10, // CELL이 10개 들어가게 할당해주세요
                        MEM_RESERVE, 
                        PAGE_READWRITE);

    printf("Reserved Addr : %p\n", pCell);

    int idx = 0;
    char s[4091];

    while(1)
    {
        printf("index >> "); scanf("%d", &idx);
        printf("data >> "); scanf("%s", s);
        strcpy(pCell[idx].data, s);         // Error - COMMIT 하지 않음.
    }
}
while(1)
{
    printf("index >> "); scanf("%d", &idx);
    printf("data >> "); scanf("%s", s);
    __try
    {
        strcpy(pCell[idx].data, s);
    }
    __except(1)
    {
        // 의도) COMMIT 되지 않은 곳을 사용하면 COMMIT되게 하겠다
        VirtualAlloc(&pCell[idx], sizeof(CELL), MEM_COMMIT, PAGE_READWRITE);
        printf("%d CELL Commit : \n", idx);
        strcpy(pCell[idx].data, s);
    }
}
# 0번 CELL에 hello 넣어주세요
$ index >> 0
$ data >> hello
0 CELL Commit

# 1번 CELL에 aaa 넣어주세요
$ index >> 1
$ data >> aaa
# ??? 여기서 1 CELL Commit이 안된다???

여기서 문제가 있는데 CELL의 크기가 4k가 아니라 4k-5라서 메모리가 할당될시
PAGE(4k) 단위로 할당이 되기에 index 1을 하더라도 5바이트 이내이면(aaa는 4바이트) PAGE가 할당이 안됨.

만약 5바이트 이상이였다면?

$ index >> 1
$ data >> hello!!
1 CELL Commit
# 정상적으로 commit됨
  • 핵심은 가상 메모리 할당은 항상 PAGE 크기 단위로 이루어진다.

더 큰 문제는

# 1번 CELL에 hello 넣어주세요
$ index >> 1
$ data >> hello
1 CELL Commit

# 0번 CELL에 world 넣어주세요
index >> 0
data >> world
# 0번 CELL이 할당이 안된다

애매하게 겹쳐있어서 1번할당시 0번까지 같이 할당해 버림.

5바이트 아끼겠다고 4k-5했는데 4k를 낭비해 버리는 결과를 가져왔다.


About Taehyung Kim

안녕하세요? 8년차 현업 C++ 개발자 김태형이라고 합니다. 😁 C/C++을 사랑하며 다양한 사람과의 협업을 즐깁니다. ☕ 꾸준한 자기개발을 미덕이라 생각하며 노력중이며, 제가 얻은 지식을 홈페이지에 정리 중입니다. 좀 더 상세한 제 이력서 혹은 Private 프로젝트 접근 권한을 원하신다면 메일주세요. 😎

Star
Useful Links