SW 개발

win32API :: 개념설명 :: 가상주소 / 물리주소 (계념+api)

. . . 2013. 3. 27. 08:40
반응형
  • md 변환완료 (190927)
  • 퍼온자료를 보기 좋게 편집하였습니다.
    • 출처 : http://blog.naver.com/zzangisaac.do

CE에서의 가상메모리 물리메모리 매핑

Windows CE는 Virtual Address기반이고, DMA는 Physical Address 입력을 통해 전송을 한다고 했습니다. 그럼 CE에서 DMA를 사용하려면 어떻게 해야할까요??

간단하게 답을 말하자면, 특정 Physical Address가 매핑되어있는 Virtual Address를 알면 됩니다. Windows CE에서 매핑된 Virtual Address에다가 어떤 스트림을 집어넣고 DMA는 Virtual Address가 가리키는 그 Physical Address를 입력받아 전송한다.

이게 기본적인 Windows CE에서의 DMA 전송방법입니다.

그럼 어떻게 Physical Memory와 Virtual Memory를 매핑시킬까요?

방법은 두가지가 있습니다.

  • 가> 특정 Physical Memory를 미리 예약해놓고 그 Physical Memory를 Virtual Memory로 매핑시키는 방법.
  • 나> Windows CE에서 제공해주는 특정 API를 통해 매핑된 Physical Address와 Virtual Address를 받는 방법.

어떤 방법이 좋을까요?

  • 가> 방법의 경우 OS를 구성할 때 미리 Memory를 Reserve해놓아야 합니다.
    • 보통은 BSP_ARGS 라는 스트럭쳐에 예약을 해놓습니다. ( INC/args.h 에 보통 있어요~ )

이 Memory는 미리 예약되어 있기 때문에 DMA를 사용하지 않을때에도 다른 시스템에서 사용할 수 없습니다.

이 방법론에서 쓰이는 함수가 바로 VirtualAlloc-VirtualCopy 함수커플과 MmMapIoSpace 함수입니다. 제가 강추하는 방법은 바로 나> 방법입니다. 나> 방법의 경우는 DMA를 위해서 특정 Memory를 미리 Reserve할 필요가 없습니다. 벌써부터 가> 방법보다 좋아보이네요 ㅎ

VirtualAlloc 과 MmMapIoSpace

VirtualAlloc과 MmMapIoSpace에 대해서 쉬울지는 모르겠지만 쓰고 싶어하시는 분들을 위해서 정리 한번 해봅니다. ^^

Device Driver를 개발하시는 분이라면 VirtualAlloc과 VitrualCopy에 대해서 너무나도 친근함을 느끼고 계시리라 믿습니다. 물론 저도 그렇고요. 하지만 우리가 망각하고 있는 MmMapIoSpace라는 강력한 놈이 존재하고 있다는거죠.

VirtualAlloc()함수와 VirtualCopy()함수의 역할은 무엇일까요? 

VirtualAlloc() 함수는 아주 간단하게 가상 메모리를 할당하는 역할을 하는 함수입니다. 정말 간단하죠 -_-;

VirtualCopy() 함수는 Physical Address를 Virtual Address에 매핑하는 역할을 합니다. Copy라고 하지만 Copy라기보단 Mapping쪽에 매우 가깝죠.

왜 저런 함수가 필요할까요?

그 이유는 우리는 직접 Physical Address 영역을 건드리지 못하기 때문입니다. 항상 Virtual Address로 접근을 해야하죠. Physical Memory 영역은 왜 쓰느냐? 라고 물어보신다면 대략난감요 ㅠㅠ; 

Physical Memory에 접근하는 경우

CPU의 Register를 컨트롤 해야하는 경우가 있고, DMA Buffer를 할당한다던가 하는 경우도 마찬가지이죠. 고정된 영역을 공유한다던가 하는 경우도 포함이구요.

Virtual Address로 접근할꺼면 그냥 메모리 맵에 매핑되있는데로 걍 0x80000000 더해서 접근하면 되지 않냐! 이놈아! 라고 하실 지 모르겠지만.. 그건 안전한 방법이 아닙니다. 물론 권장하는 방법도 아니구요. MS에서 우리에게 이 함수를 통해 말하고 싶은 건 Physical 영역을 직접 접근하지 말고 OS로부터 할당을 받아 그 Phisical 영역에 접근하고 다 끝나면 VirtualFree,... 를 통해 안전하게 메모리를 해제하라.. 라는 것입니다. 이렇게 가상할당 함수를 잘 사용하고 메모리 해제를 잘 시켜주면. 시스템을 좀 더 안전하게 꾸릴 수 있습니다.

VirtualAlloc과 VirtualCopy 쓸때 주의점...

VirtualAlloc과 VirtualCopy의 크로스에는 치명적인 결함(?)이 있었으니.. 그것은 VirtualCopy라는 놈의 두번째 인자값.. 저 lpvSrc라는 놈때문입니다. lpvSrc는 Src Address. 즉 PHYSICAL ADDRESS 값을 말합니다. lpvSrc가 피매핑되는 물리적영역인거죠.

BOOL VirtualCopy()

그 이유는 저 lpvSrc에 들어가는 Physical Address가 8Bit Align이 되어야 하기 때문입니다. 즉.. 256 Align이 되어야 합니다. 이 말은 곧 256align이 되지 않는 Physical Address는 저 두개의 함수로 바로 쓰지 못하게 된다는 겁니다. 보통은 이럴 경우를 대비해서 AlignAddress와 OffsetAddress로 나누어서 256 align을 맞춘 후 offset을 더해주어 사용합니다.

자 그 예가 여기 있습니다.

U32 OffsetAddr, AlignAddr;
AlignAddr = PHY_BASEADDR & 0xfffff000;
OffsetAddr = PHY_BASEADDR & 0x00000fff;
m_pRegSet = (DWORD*)VirtualAlloc(0, dwSizeRegSet, MEM_RESERVE, PAGE_NOACCESS);
if(!m_pRegSet)
{
    DEBUGMSG(1, (TEXT("m_pRegSet: VirtualAlloc failed!\\r\\n")));
    return(CFALSE);
}
if (!VirtualCopy((PVOID)m_pRegSet, (PVOID)(AlignAddr >> 8),
   dwSizeRegSet, PAGE_PHYSICAL | PAGE_READWRITE | PAGE_NOCACHE))
{
    DEBUGMSG(1, (TEXT("m_pRegSet: VirtualCopy failed!\\r\\n")));
    return(CFALSE);
}
m_pRegSet = (DWORD*)((U32)m_pRegSet + OffsetAddr );

VirtualFree(m_pRegSet,dwSizeRegSet,MEM_RELEASE );

Align을 0xfffff000으로 한 이유는 좀 더 안전하려고... (퍽!) -_-;

어쨌든 Offset을 더해서 다시 RegSet을 설정합니다.  이코드에는 한가지 문제가 발생합니다. 흠흠.. 뭐냐면.. Memory가 휭하고 새버립니다. 물론 안새게 할 수도 있지만.. 대부분의 Coder들이 망각하고 넘어갑니다.

OffsetAddress만큼 더해준 Regset을 VirtualFree를 통해 Free를 시키게 되면 m_pRegSet은 Free가 되지만.. 처음에 할당한 Virual Address가 해제된 것이 아니라 처음 할당한 Address + OffsetAddress 의 포인터부터 메모리 해제가 되버립니다. 그렇게 되면 Offset값만큼의 메모리가 사라지죠. 세트를 만드시는 분들은 메모리가 새는 테스트를 해보셨을 거라 생각됩니다만... 사실 이런 부분에서 새는 경우가 참 많습니다. CPP의 경우는 new 해주고  delete안해서 새는 경우가 다반사이고.. C의 경우는 이런 경우가 자주 발생합니다. 이런 걸 다 신경써서 코딩해야 합니다.

MmMapIoSpace()

우리의 친절한 MS께서는 우리의 이 귀찮음과 스트레스를 열납하시어.. 그이름도 뷁스러운 M자가 연속3개나 이어지는 MmMapIoSpace() 라는 사랑스러운 함수를 내려 주셨습니다. 참 발음하기 구리죠. 엠엠맵아오스뷁 -_-;

이 함수는 너무나도 친절하게 VirtualCopy처럼 256align을 맞출 필요가 없습니다. !!!!

그리고 VirtualAlloc과 VirtualCopy의 두개함수가 합쳐진 기능이구요.. 또한!! VirtualAlloc, VirtualCopy처럼 뒤에 붙는 Flag들을 신경쓸 필요도 없습니다. ^^

PHYSICAL_ADDRESS AllocRegisterAddress;
AllocRegisterAddress.LowPart = (DWORD)(PHY_BASEADDR);
m_pRegSet = (DWORD*)MmMapIoSpace(AllocRegisterAddress,dwSizeRegSet, FALSE); 
if(!m_pRegSet)
{
    RETAILMSG(1, (TEXT("MmMapIoSpace failed!\\r\\n")));
} 

저 위의 코드를 이렇게 짧게 해결했습니다. ㅋㅋ

기능은 같습니다 VirtualAlloc과 VirtualCopy를 단 하나의 함수로 쉽게 해결을 한거죠. 단지 함수 인자로 들어가는 것들이 약간 다릅니다. 여기서는 PHYSICAL_ADDRESS 스트럭쳐를 받게 됩니다.  FALSE는 Cache영역의 메모리를 할당할것인가에 대한 겁니다. FALSE는 NON_Cache영역을 지칭합니다. 이것만 유의하시면 똑같이 사용할 수 있습니다.

MmUnmapIoSpace(m_pRegSet , dwSizeRegSet );

메모리 해제

메모리 해제의 경우도 완전 간단합니다. 이놈은 Return값도 void입니다. 걍 무조건 해제하는거죠 -_-

에러따위는 사치다! 라는 것일까요.. 쨌든 리턴값이 없습니다. -_-;

이렇게 보시면 훨씬 쉽다는 것을 알 수 있습니다.

VirtualAlloc과 VirtualCopy를 사용할 경우 신경써야하는 여러가지 FLAGS들과 메모리 주소를 기억해야 하는 문제등등의 번거로움을 한큐에 해결할 수 있습니다.

반응형