티스토리 뷰

오늘은 virtualAllloc과 virtualFree 함수를 공부하였습니다. 자세한 내용은 

 

아래는 Microsoft 공식문서에 있는 두 함수의 원형입니다. 아래 더보기는 각 함수에 대한 설명입니다.

LPVOID VirtualAlloc(
  [in, optional] LPVOID lpAddress,
  [in]           SIZE_T dwSize,
  [in]           DWORD  flAllocationType,
  [in]           DWORD  flProtect
);
더보기

첫 번째 인자

- 먼저 공통적으로 첫 번째 인자는, 시작할 주소 또는 해제할 주소의 시작지점을 인자로 받습니다. 시작 지점이 없이 랜덤한 공간을 할당받을 경우 NULL을 입력하면 됩니다.

두 번째 인자

- 두 번째로, 할당할 사이즈의 크기입니다.

세 번째 인자

- Alloc할때의 설정입니다.

MEM_COMMIT : 접근하기 위한 설정

MEM_RESERVE : 메모리 또는 디스크의 페이징 파일에 실제 물리적 저장소를 할당하지 않고 프로세스의 가상주소 공간 범위를 예약. 예약된 페이지를 커밋 즉 접근하려면 MEM_COMMIT | MEM_RESERVER

(malloc 및 LocalAlloc과 같은 다른 메모리 할당 함수는 해제될 때까지 예약된 메모리 범위 사용 X)

MEM_RESET : 지정된 공간에 대하여 수정사항이 없다고 커널에 알려준다. 다만 다른 설정과 함께 사용 X

(사용자가 지정한 곳 이외의 영역에 중요햐ㅏㄴ 데이터가 있을 경우 이를 보호하기 위함)

 

MEM_LARGE_PAGES : CPU에서 큰 페이지 할당 기능을 제공할 경우 큰 페이지 할당을 사용. 다만 조건이 존재

MEM_TOP_DOWN : 영역의 예약 시 상위주소부터 예약하도록 한다.

MEM_WRITE_WATCH : 해양 영역에 쓰기 작접이 발생하였을 때 감시하기 위해 사용

네 번째 인자

- 할당할 페이지 영역에 대한 메모리 보호 설정

PAGE_READWRITE : 쓰기 읽기

이외 :  https://learn.microsoft.com/en-us/windows/win32/Memory/memory-protection-constants

BOOL VirtualFree(
  [in] LPVOID lpAddress,
  [in] SIZE_T dwSize,
  [in] DWORD  dwFreeType
);
더보기

첫 번째 인자

- 먼저 공통적으로 첫 번째 인자는, 시작할 주소 또는 해제할 주소의 시작지점을 인자로 받습니다. virtual Alloc 같은 경우, 시작 지점이 없이 랜덤한 공간을 할당받을 경우 NULL을 입력하면 됩니다.

두 번째 인자

- 두 번째로, 해체할 메모리의 크기입니다. 다만, 여기서 주의할 점은 VirtualFree같은 경우 VirtualAlloc에서 속성이 MEM_RELEASE일 경우, 0으로 입력하여야 합니다.

세 번째 인자

MEM_DECOMMIT : 메모리 해제는 하지 않고, 커밋을 해제

MEM_RELEASE : 지정된 페이지 영역을 해제. 이 값을 지정할때는 dw_size가 0이여야 함. 그리고 lpAddress가 시작 지점을 가리켜야함. 만약 둘다 아닐 경우 함수는 실패

 

아래 코드는 예시이며, 특히 Virtual Alloc 코드는, BufferOverFlow를 방지하기 위한 작업이 하나 추가되어 있습니다.

 

1번 예시와 2번 예시가 존재합니다. 1번 예시 같은 경우, 단순하게 사용하였을 경우이며, 두 번재 예시는 BufferOverFlow를 방지하기 위한 코드입니다.

 

1번 예시

class StompAllocator
{
	enum { PAGE_SIZE = 0x1000 };
public:
	static void* Alloc(int32 size);
	static void Reelase(void* ptr);
};

void* StompAllocator::Alloc(int32 size)
{
	const int64 pageCount = (size / PAGE_SIZE) + 1 / PAGE_SIZE;

	return ::VirtualAlloc(NULL, pageCount * PAGE_SIZE, MEM_RELEASE | MEM_COMMIT, PAGE_READWRITE);
}


void StompAllocator::Reelase(void* ptr)
{
	VirtualFree(ptr, 0, MEM_RESERVE);
}

먼저 1번 예시는 단순하게 virtual Alloc을 사용하였을 때 입니다.

 VirtualAlloc 특성 상 PAGE라는 CPU에 있는 특정 크기만큼 할당하고, 이 크기는 CPU에 따라 다릅니다.

이 예시의 단점은, 반환된 void* ptr을 사용할 때, size가 32여도 기본 0x1000의 크기로 할당하기 때문에 32보다 큰 값을 넣어도 문제가 발생하지 않는다는 단점이 존재합니다.


2번 예시

class StompAllocator
{
	enum { PAGE_SIZE = 0x1000 };
public:
	static void* Alloc(int32 size);
	static void Reelase(void* ptr);
};

void* StompAllocator::Alloc(int32 size)
{
	const int64 pageCount = (size / PAGE_SIZE) + 1 / PAGE_SIZE;
	const int64 dataOffset = pageCount * PAGE_SIZE - size;
	void* baseAddress = ::VirtualAlloc(NULL, pageCount * PAGE_SIZE, MEM_RELEASE | MEM_COMMIT, PAGE_READWRITE);

	return static_cast<void*>(static_cast<char*>(baseAddress) + dataOffset);
}


void StompAllocator::Reelase(void* ptr)
{
	const int64 address = reinterpret_cast<int64>(ptr);
	const int64 baseAddress = address - (address % PAGE_SIZE);
	::VirtualFree(reinterpret_cast<void*>(baseAddress), 0, MEM_RELEASE);
}

2번 예시는 위에 1번 예시에 발생한 OverFlow 문제를 막고자, 동적으로 할당한 후, 시작지점 Address에 DataOffset을 구해 더한 위치를 리턴합니다. 이렇게 되면 아래 그림처럼 위치를 바꿔서 실제 크기에 해당하는 부분만 사용할 수 있도록 위치를 변경 후 반환하여 줍니다.

[     ] - 실제 크기

[-                         ] - 원래 반환되는 위치(-)

[                    -     ] - 실제 반환되는 위치(-)

 

'프로그래밍 언어 > C++' 카테고리의 다른 글

MemoryPool #1  (0) 2023.04.05
STLAllocator  (0) 2023.04.02
placement new문법(객체 초기화)  (0) 2023.03.27
shared_ptr 구현기 - 1일차  (0) 2023.03.27
C++) STL 목차 정리  (0) 2022.11.24
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함