가상 메모리 사용
|-----------------------|
| |
| 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를 낭비해 버리는 결과를 가져왔다.