ZZ: http://www.cnblogs.com/lzjsky/archive/2010/11/19/1881911.html
In different scenarios, many driver writers need to share the memory between the driver and the user program. The two easiest technologies are:
1. The application sends IOCTL to the driver and provides a pointer to the memory. Then the driver and application can share the memory. (Application allocates shared memory)
2. The driver allocates memory pages, maps these memory pages to the address space of the specified user mode process, and returns the address to the application. (Drivers allocate shared memory)
Use IOCTL to share the buffer:
Using a buffer described by ioct, Memory sharing between the driver and the user program is the simplest form of Memory Sharing. After all, IOCTL is also the most typical method to drive support for other I/O requests. The application calls the Win32 function deviceiocontrol (). The base address and length of the buffer to be shared are put into the outbuffer parameter. For drivers using this buffer sharing method, you need to determine which buffer method is used for the specific IOCTL. You can use either method_xxx_direct or method_neither.
(PS: In method_xxx_direct mode, the IO manager creates an MDL for the output buffer (outputbuffer) specified by the application layer to lock the buffer memory of the application layer. Then, we can use mmgetsystemaddressformdlsafe in the kernel layer to obtain the kernel layer address corresponding to the application layer output buffer. The MDL address is placed in IRP-> mdladdress ).
If method_xxx_direct is used, the user buffer will be checked for correct access. After the check, the user buffer will be locked into the memory. The driver needs to call mmgetsystemaddressformdlsafe to map the preceding buffer to the kernel address space. One advantage of this method is that the driver can access the shared memory buffer at any process context and any IRQL priority level. If you only need to pass data to the driver, use the method_in_direct method. If data is returned from the driver to the application or two-way data exchange is performed, method_out_buffer is used.
(PS: these checks will be handled by the IO manager, and the IO manager will create MDL for the user-layer buffer. Because the device driver layer is not yet reached, the current context is still the process that initiates the deviceio call, and the memory in the user mode buffer is valid. However, when I/O manager sends an IRP to the underlying driver, the current context cannot be guaranteed. Fortunately, I/O manager creates MDL for us. In this way, we can obtain the corresponding kernel address at the kernel layer and write data freely .)
The method_neither method is used to describe the existence of many inherent limitations and precautions for a shared memory buffer. (Basically, a driver uses the same method at any time ). The main rule is that the driver can only access the buffer in the context where the request process is initiated. This is because the User Virtual Address of the buffer is used to access the shared memory buffer. This means that the driver must be at the top of the device stack and be directly called by the user application via Io manager. During this period, the middle-layer driver or file system driver cannot exist on top of our driver. In practice, the WDM driver will strictly limit the storage of user buffer in its dispatch routine. The kmdf driver must be used in the evtioincallercontext Event Callback Function.
Another important constraint is that drivers using the method_neither method must be at the passive_level IRQL level to access the user buffer. This is because Io manager does not lock the buffer in the memory. Therefore, when the driver wants to access the shared buffer, the memory may be swapped out. If the driver cannot meet this requirement, the driver needs to create an MDL and then lock the shared buffer into the memory.
(PS: method_neither is not recommended. It is better to use direct Io .)
In addition, considering the selection of transmission types, the possible non-direct and obvious limitation of this method is that shared memory must be allocated by user-mode applications. Given the quota limit, the amount of memory that can be allocated is limited. In addition, user applications cannot allocate physical continuous memory and non-cache memory. Of course, if all you need to do for the driver and user mode applications is to use a reasonable data buffer to pass in and out the data, this technology may be the simplest and most practical.
Similar to its simplicity, IOCTL is also the most common misunderstanding of Memory sharing between drivers and user-mode applications. A common mistake for new Windows Driver developers who use this solution is to notify the end of IOCTL when the driver has queried the buffer address. This is a very bad thing. Why? If the application suddenly exits, for example, what will happen. Another problem is that when method_xxx_direct is used, if the IRP with MDL is completed, the buffer will not be mapped to the system kernel address space, an attempt to access the previously valid kernel virtual address space (obtained by mmgetsystemaddressformdlsafe) will cause the system to crash. This is usually avoided.
One solution to this problem is that the application uses file_flag_overlapped to open the device and considers IOCTL to use an overlapped structure. A driver can set the cancel routine for IRP (using iosetcancelroutine), mark the IRP as pending (using iomakeirppending), and put the IRP into the internal queue before returning it to the caller status_pengding. Of course, the kmdf driver can be assured of this type of problem, just set the request to in progress and can be canceled, just like wdfqueue.
(PS: Be careful when using MDL to prevent unexpected exit of the Application Layer Program from invalidating the virtual memory described by MDL .)
This method has two advantages:
1. When the application obtains the returned result of error_io_pending from The IOCTL call, the buffer is mapped. It also knows when IOCTL will be completed and the buffer will be unmapped.
2. When the driver successfully exits or cancels the IO command, the callback routine is handled by the cancel routine (WDM) or an evtiocancelonqueue event, therefore, it can perform the necessary operations to complete IOCTL. Therefore, the MDL position is used for memory un ing.
Assignment and ing page:
Now there is the second method mentioned above: allocate memory pages and map these pages to the user virtual address space of a specific process. Using the common APIs of most Windows drivers, this method is surprisingly easy and allows the driver to have maximum control over the type of memory allocated.
No matter what standard method is used, the driver wants to allocate memory for sharing. For example, if the driver needs an appropriate device (logical) address for DMA, like the kernel virtual address of the memory block, it can use allocatecommonbuffer to allocate memory. If specific memory features are not required, the size of the memory to be shared is also moderate. the driver can allocate 0-padding and non-Paging physical memory pages to the buffer.
Allocate 0-Filling pages from the primary memory and use mmallocatepagesformdl or mmallocatepagesformdlex for non-Paging pages. These functions return an MDL describing the memory allocation. The driver uses the mmgetsystemaddressformdlsafe function to map the pages described by MDL to the kernel virtual address space. It is not a good idea to obtain more secure memory from the primary memory allocation page than using the paging memory pool or non-Paging memory pool.
PS: This method is used by the kernel to allocate memory space. However, if mmallocatepagesformdl is used to allocate memory from the master memory pool, an MDL is returned. For how the driver uses the shared memory, mmgetsystemaddressformdlsafe is used to obtain the kernel address. For the application layer to use the shared memory, use mmmaplockedpagesspecifycache to map to the application layer process address space, return the starting address of the user layer address space, put it in ioctl, and return it to the user application.
With an MDL used to describe shared memory, the driver is now ready to map these pages to the user process address space. This can be achieved using the mmmaplockedpagesspecifycache function. You need to know the tips for calling this function:
You must call this function in the context of the process in which you want to map the buffer.
PS: If it is called in another process context, it will be mapped to another process context. But how can I ensure that the process context in which I want to map the buffer is called?
Set the accessmode parameter to usermode. The returned value for the mmmaplockedpagesspecifycache function call is the user virtual address space address mapped to the Memory Page described by MDL. The driver can put it in the cache of the corresponding IOCTL for the user application.
You need to clear the allocated memory when you do not need it. In other words, you need to call mmfreepagefrommdl to release the Memory Page. Call iofreemdl to release the MDL created by mmallocatepageformdl (Ex. Almost all of you do this in your driver's irp_mj_cleanup processing routine (WDM) or evtfilecleanup event processing callback (kmdf ).
This is all you need to do. For the code to complete this process, see the following.
Pvoid createandmapmemory (Out pmdl * pmemmdl, out pvoid * userva) {pmdl MDL; pvoid parent; lowaddress; physical_address highaddress; size_t totalbytes; // initialize the physical addresslowaddress required by the parent. quadpart = 0; max_mem (highaddress. quardpart); totalbytes. quadpart = page_size; // allocate a 4 K shared buffer MDL = mmallocatepagesformdl (lowaddress, highaddress, lowaddress, totalbytes); If (! MDL) {return region;} // ing the shared buffer to the user address space uservatoreturn = mmmaplockedpagesspecifycache (MDL, usermode, mmcached, null, false, normalpagepriority); If (! Uservatoreturn) {mmfreepagesfrommdl (MDL); iofreemdl (MDL); Return success ;}// return the virtual address * userva = uservatoreturn; * pmemmdl = MDL; return STATUS_SUCCESS ;}
Of course, this method also has disadvantages. Calling mmmaplockedpagesspecifycache must be done in the process context where you want the memory page to be mapped. Compared with the ioctl method using method_neither, this method shows no more flexibility than it. However, unlike the former, the latter requires only one function (mmmaplockerpagesspecifycache) to be called in the target context. Many OEM device drivers have only one and are directly bus-based in the device stack (that is, there is no other device on it, except that there is no driver under the bus driver ), this condition is easily met. For a small number of device drivers, they are in the depth of the device stack and need to be directly shared with user-mode applications.
Buffer, an enterprise-level driver writer may find a safe place to call in the context of the request process.
After the page is mapped, the shared memory can be accessed in any process context like using the ioctl method of method_xxx_direct, it can also be accessed on high IRQL (because the shared memory is not paging memory ).
PS: we need to determine when to call mmmaplockedpagesspecifycache to securely map to the context of the specified process. Another point is that the shared memory is in non-Paging memory, so it can be accessed on high IRQL.
If you use this method, there is always a decisive thing to remember: You must be sure that your driver will provide the method, at any time when the user process exits, you can map the page to the user space for un ing. This failure will cause the system to crash when the application layer exits. A simple method is to cancel the ing operation on these pages whenever the application layer closes the device handle. Because the application layer closes the handle, the driver will receive an irp_mj_cleanup corresponding to the device file object opened at the application layer, and you can be sure this is working. You will execute these operations in cleanup, instead of close, because you can ensure that cleanup is obtained in the context of the request thread.
IRP. The following code shows the release of the allocated resource.
Void unmapandfreememory (pmdl, pvoid userva) {If (! Pmdl) {return ;}// un ing mmunmaplockerpages (userva, pmdl); // release the MDL-locked physical page mmfreepagesfrommdl (pmdl); // release mdliofreemdl (pmdl );}
Other challenges:
No matter which mechanism is used, drivers and applications will need to support a common method for Synchronous access to shared memory, which can be done in many ways. The simplest mechanism is to share one or more naming events. The simplest way to drive shared events is to generate events at the application layer and pass the event handle to the driver layer. The driver then references the event handle from the context of the application layer. If you use this method, do not forget the dereference handle in the driver's cleanup processing code.
PS: Be sure to reference event objects from the application layer.
Summary:
We have observed two ways for applications to share memory in driver and user mode:
1. the user layer creates a buffer and passes it to the driver through IOCTL.
2. In the driver, use mmallocatepagesformdl to allocate the Memory Page to obtain the MDL, and then map the memory described by the MDL to the user-layer address space (mmmaplockedpagesspecifycache ). Obtain the starting address of the user address space and return it to the user layer through IOCTL.
Note:
When using a naming event to synchronize the buffer shared by the driver and the application, do not use the driver to create a naming event, and then open the event based on the Application name. Although this method can make all related applications wake up after the driver activates the event to facilitate program development, there are two problems: first, naming events can be correctly created only after the Win32 subsystem is up, which affects driver development. The most serious problem is that the access permissions of events created in the driver are relatively high. In WINXP, Applications created by users with Administrator group permissions can access the event. In the Vista system, due to enhanced security functions, this problem is more serious. Therefore, try to use the events created by the application, or use other Synchronization Methods.