본컬럼에대한모든저작권은 DevGuru에있습니다. 컬럼을타사이트등에기재및링크또는컬럼내용을인용시반드시출처를밝히셔야합니다. 컬럼들을 CD나기타매체로배포하고자할경우 DevGuru에동의를얻으셔야합니다. c DevGuru Corporation. All rights reserved 기타자세한질문사항들은웹게시판이나 support@devguru.co.kr 으로 문의하기바랍니다. http:www.devguru.co.kr - 1 -
c 2003 Devguru ( Device driver Guru ), Inc. 대부분디바이스드라이버프로그래머들은 user-mode Apllication 과드라이버간에메모리를공유하기를원하였고, 여러가지다양한방법으로 user-mode Apllication 과드라이버간의메모리공유를하였다. 다음은가장쉬운 user-mode Apllication 과드라이버간의메모리공유기술이다 : - Apllication은 IOCTL Code를이용하여 Driver와공유할 buffer의 pointer를 Driver에게보낸다. 그후그 buffer를공유한다. - Driver는 memory block을할당하고, memory block을특정 user-mode Process의주소공간으로변환한다. 그후이주소를 application에게 return 한다. 이방법외에도몇개의다른방법으로 memory sharing 을구현할수있다. (paging file or memory mapped file) IOCTL 을이용한 Buffer 공유방법 : IOCTL 은 Driver 와 user-mode 간의메모리공유방법에서가장간단한방법이다. Application에서할당받은공유할 buffer의 base address와길이를 DeviceIoControl() 의매개변수로하여호출한다. IOCTL 을이용한방법에서두 2가지의 transfer type을사용하여메모리공유를할수있다. - METHOD_DIRECT( using MDL ) 이 transfer type을사용한다면 user buffer는 locked 된상태이고 driver는이 buffer를사용하기위하여 MmGetSystemAddressForMdlSafe() 함수를이용하여 Kernel virtual address space안에 mapping 되어있는 address를얻어서사용한다. 이 transfer type을사용한다면 driver는어떠한 IRQL과어떠한 process( arbitrary process ) 에서사용가능하다. - METHOD_NEITHER 이 transfer type을이용한메모리공유방법에는몇가지제한과경고를가지고있다. Driver는반드시요청을내려보낸 process의 context안에서 buffer를접근해야한다. 그이유는 buffer는 user virtual address space에존재하기때문이고, 이로인하여 driver는 device stack 중에가장위단 (top of stack) 에존재해야한다. top of stack Driver 보다아래 ( 하위 layer) 단의 intermediate of file system drivers는사용할수없으며항상 PASSSIVE_LEVEL에서만사용가능하다는것이다. 그이유는 I/O Manager 이 user http:www.devguru.co.kr - 2 -
buffer를 locked 시키지않기때문에언제 page out이일어날지모르는일이다. 이메모리를 lock 시키고싶다면 MDL을사용해야할것이다. 또다른재약은 User application은 non-cached memory 또는 physically contiguous memory 를할당할수없다는것이다. IOCTLs를이용한방법에는조심해야 ( 하지말아야 ) 할사항들이있다. IOCTL를 Complete 한후에 buffer를접근해서는안된다. Application이갑자기종료되어을때 Driver는무심코전혀상관없는 memory에 overwrite 하게되고언제가는 system crash 일어날것이다. 또하나는 METHOD_DIRECT를사용할때, MDL을포함한 Irp를 complete 시킨후이전에 MmGetSystemAddressForMdlSafe() 함수를이용하여얻은 Kernel virtual address를접근하려고시도하려할때이것은 system crash를발생할것이다. Driver writter들은이러한문제점들을피해야한다. Example) Application : #define DEV_SHAREMEM CTL_CODE(FILE_DEVICE_UNKNOWN,0x800, METHOD_NEITHER, FILE_ANY_ACCESS) main() { BYTE * pbuffer; Pbuffer = malloc(); DeviceIoControl(,pbuffer, size, ); Driver : CTL_CODE(FILE_DEVICE_UNKNOWN,0x800, METHOD_NEITHER, FILE_ANY_ACCESS) DispatchDeviceIoControl() { BYTE * psharemem = pirp->userbuffer; http:www.devguru.co.kr - 3 -
Mapping Kernel Memory To User Mode: 두번째방법에서는 Kernel mode 에서 buffer 를할당받고, 특정 process 의 user virtual address spacce 에 mapping 을하는방법이다. 이방법은몇개의 Kernel mode 함수를이용하여아주간단하게구현할수있다. 드라이버는공유할메모리를할당할것이다. 특정목적에사용사용할것이라면, 예들들어 DMA 전송을위하여사용할것이라면 AllocateCommonBuffer() 를이용할것이다. 그러나별다른특정목적이없다면 non-pagepool한 memory 영역을할당할것이다. 뒤이어 IoAllocateMdl() 을이용하여 buffer를지시할 ( 설명할 ) MDL을할당할것이며추가적으로 I/O Manager의 look-aside list에 MDL의 fixed part 부분을을할당할것이다. 다음으로 MDL의 variable part위하여 MmBuildMdlForNonPagedPool() 함수를호출할것이다. 특정, 현재 context를가지고있는 process (Kenerl memory를공유하고싶어하는 Application) 의주소로 mapping하기위하여 MmMapLockedPagesSpecifyCache()( Win2K를위해 ) 또는 MmMapLockedPage() ( NT V4를위해 ) 함수를이용할것이다. MmMapLocedXXX() 함수는반드시 buffer를 mapping 하기원하는 context내에서호출되어져야만한다. 아래예제를 Kernel mode memory address 를이용하여 user mode 쪽으로 mapping 하는예제입 니다. PVOID CreateAndMapMemory() { PVOID buffer; PMDL mdl; PVOID uservatoreturn; Allocate a 4K buffer to share with the application buffer = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, 'ShareMem'); http:www.devguru.co.kr - 4 -
if(!buffer) { return(null); Allocate and initalize an MDL that describes the buffer mdl = IoAllocateMdl(buffer, PAGE_SIZE, FALSE, FALSE, NULL); if(!mdl) { ExFreePool(buffer); return(null); Finish building the MDL -- Fill in the "page portion" MmBuildMdlForNonPagedPool(mdl); #if NT_40 Map the buffer into user space NOTE: This function bug checks if out of PTEs uservatoreturn = MmMapLockedPages(mdl, UserMode); #else http:www.devguru.co.kr - 5 -
The preferred V5 way to map the buffer into user space uservatoreturn = MmMapLockedPagesSpecifyCache(mdl, MDL UserMode, Mode MmCached, Caching NULL, Address FALSE, Bugcheck? NormalPagePriority); Priority If we get NULL back, the request didn't work. I'm thinkin' that's better than a bug check anyday. if(!uservatoreturn) { IoFreeMdl(mdl); ExFreePool(buffer); return(null); #endif Store away both the mapped VA and the MDL address, so that later we can call MmUnmapLockedPages(StoredPointer, StoredMdl) StoredPointer = uservatoreturn; StoredMdl = mdl; DbgPrint("UserVA = 0x%0x n", uservatoreturn); return(uservatoreturn); http:www.devguru.co.kr - 6 -
이방법에서또한손실을안고있다. MmMapLockedXXX() 함수는만드시 mapping 하기원하는 process context 내에서호출이되어져야하기때문이다. 우리가처음에얘기했던 IOCTL 의 METHODE_NEITHER 방법보다더 flexible 하지못하다. 그러나이방법은 MmMapLockedXXX() 함수만이해당 process context 내에서호출이되어지면되고, top of stack 에서만사용가능했던 IOCTL 과는틀리게그보다더아래단에서두사용이가능하다. 많은 OEM Device 들의 Driver는 top of stack이아니다. 이방법을사용하면반드시 page 를 unmap 시켜야한고이일은 IRP_MJ_CLOSE 에서보다는 IRP_MJ_CLEANUP 에서해야한다. 그이유는 IRP_MJ_CLEANUP 은 requesting thread 내에서실행 되어진기때문이다. 지금까지우리는 kernel mode와 user mode간의메모리공유에대한두가지방법을살펴보았다. 첫번째는 user mode에서 buffer를할당하고 IOCTLs를이용하여 kernel Mode로내려보내는방법. 두번째는 Kernel mode에서 buffer를할당하여특정 process context( 우리가공유하려는 user-mode application) 에서 MmMapLockedXXX() 함수를사용하는방법이다. 비교적두방법모두간단하며, 몇가지의 rule만지키면된다. 본 column 에대한문의사항은저희홈페이지 QnA 을이용하시면됩니다. http:www.devguru.co.kr - 7 -