Địa chỉ một Bộ đệm dữ liệu (Addressing a Data Buffer )
Khi một ứng dụng bắt đầu thao tác đọc hay ghi, nó sẽ cung cấp một vùng đệm dữ liệu thông qua cách cung cấp cho bộ quản lý vào/ra một chế độ người dung địa chỉ ảo và chiều dài của nó. Như tôi đã trình bày ở trong chương 3 trước đây, một nhân điều khiển rất ...
Khi một ứng dụng bắt đầu thao tác đọc hay ghi, nó sẽ cung cấp một vùng đệm dữ liệu thông qua cách cung cấp cho bộ quản lý vào/ra một chế độ người dung địa chỉ ảo và chiều dài của nó. Như tôi đã trình bày ở trong chương 3 trước đây, một nhân điều khiển rất hiếm khi truy xuất vào bộ nhở mà sử dụng chế độ người dung với địa chỉ ảo bởi vì, nhìn chung, bạn chắc chắn không thể chốt được các luồng. Microsoft Windows XP cung cấp cho bạn 3 cách để truy xuất một bộ đệm dữ liệu ở chế độ người dung:
- Trong phương thức Buffered, trình quản lý vào /ra tạo ra một bộ đệm hệ thống bằng với kích thước của bộ đệm dữ liệu chế độ người dung. Và bạn làm việc với bộ đệm hệ thống này. Trình quả lý vao/ra sẽ giám sát cẩn thận (trông nom) việc copy dữ liệu giữa bộ đệm chế độ người dùng và bộ đệm hệ thống.
- Trong phương thức Direct, trình quản lý vào/ra khoá các trang vật lý đang chứa đựng bộ đệm chế độ người dùng và tạo ra một cấu trúc dữ liệu phụ được gọi là danh sách kí hiệu bộ nhớ (MDL) để diễn tả các trang bị khoá. Khi đó bạn sẽ làm việc với MDL.
- Phương thức neither, trình quản lý vào ra đơn giản là cho bạn vượt qua địa chế độ địa chỉ người dùng. Bạn cần phải làm việc hết sức cẩn thận với địa chỉ chế độ người dùng/
Hình 7.2 minh hoạ hai phương thức đầu. Phương thức cuối cùng, tất nhiên là phương thức mà hệ thống không làm gì cả để có thể giúp đỡ bạn đến gần (lấy được) dữ liệu của bạn.
Hình 7-2. Accessing user-mode data buffers.
Chỉ rõ một phương thức vùng đệm (Specifying a Buffering Method):
Bạn tiến hành định rõ một phương thức vùng đệm để đọc hay ghi bằng cách thiết lập các bit cờ trong đối tượgn thiết bị của bạn ngay sau khi bạn tạo ra hàm AddDivice:
NTSTATUS AddDevice(...)
{
PDEVICE_OBJECT fdo;
IoCreateDevice(..., &fdo);
fdo->Flags │= DO_BUFFERED_IO;
<or>
fdo->Flags │= DO_DIRECT_IO;
<or>
fdo->Flags │= 0; // i.e., neither direct nor buffered
}
Bạn không thể thay đổi ý định của mình về phương thức vùng đệm sau đó. Các trình điều khiển lọc có thể copy thiết lập cờ này và sẽ không có cách nào để biết được nếu bạn thực hiện thay đổi của mình và định ra một phương thức vùng đệm khác,
Phương thức của vùng đệm (The Buffered Method):
PVOID uva; // <== user-mode virtual buffer address
ULONG length; // <== length of user-mode buffer
PVOID sva = ExAllocatePoolWithQuota(NonPagedPoolCacheAligned,
length);
if (writing)
RtlCopyMemory(sva, uva, length);
Irp->AssociatedIrp.SystemBuffer = sva;
PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(Irp);
if (reading)
stack->Parameters.Read.Length = length;else
stack->Parameters.Write.Length = length;
<code to send and await IRP>
if (reading)
RtlCopyMemory(uva, sva, length);
ExFreePool(sva);
Phương pháp trực tiếp (The Direct Method):
Nếu trong đối tượng thiết bị bạn chỉ rõ DO_DIRECT_IO thì trình điều khiển vào ra sẽ tạo ra một MDL để mô tả các trang bị khoá đang chứa vùng đệm dữ liệu chế độ người dùng.Cấu trúc của MDL được khai báo như sau:
typedef struct _MDL {
struct _MDL *Next;
CSHORT Size;
CSHORT MdlFlags;
struct _EPROCESS *Process;
PVOID MappedSystemVa;
PVOID StartVa;
ULONG ByteCount;
ULONG ByteOffset;
} MDL, *PMDL;
Hình 7.3 minh hoạ cho quy luật của MDL. Thành phần StartVa cung cấp địa chỉ ảo của vùng đệm - chỉ hợp lý trong trường hợp chế độ người dùng xử lý điều này. ByteOffset là địa chỉ offset của nơi bắt đầu vùng đệm trong suốt một Frame, và ByteCount là kích thước của vùng đệm tình theo đơn vị Byte. Mảng Page, không được khai báo thông thường như phần cấu trúc của MDL, tiếp theo MDL trong bộ nhớ và chứa đựng số lượng các trang Frame vật lý của bản đồ địa chỉ ảo ở chế độ người dùng.
Hình 7.3. Liệt kê cấu trúc kí hiệu (thể hiện) của bộ nhớ.
Theo cách này, chúng ta chẳng bao giờ truy cập trực tiếp tới các thành phần của cấu trúc MDL. Chúng ta sử dụng các Macro và nâng cấp các hàm như trong bảng 7.2
Table 7-2. Macros and Support Functions for Accessing an MDL | |
Macro or Function | Description |
IoAllocateMdl | Creates an MDL. |
IoBuildPartialMdl | Builds an MDL for a subset of an existing MDL. |
IoFreeMdl | Destroys an MDL. |
MmBuildMdlForNonPagedPool | Modifies an MDL to describe a region of kernel-mode nonpaged memory. |
MmGetMdlByteCount | Determines byte size of buffer. |
MmGetMdlByteOffset | Gets buffer offset within first page. |
MmGetMdlPfnArray | Locates array of physical page pointers. |
MmGetMdlVirtualAddress | Gets virtual address. |
MmGetSystemAddressForMdl | Creates a kernel-mode virtual address that maps to the same locations in memory. |
MmGetSystemAddressForMdlSafe | Same as MmGetSystemAddressForMdl but preferred in Windows 2000 and later systems. |
MmInitializeMdl | (Re)initializes an MDL to describe a given virtual buffer. |
MmMapLockedPages | Creates a kernel-mode virtual address that maps to the same locations in memory. |
MmMapLockedPagesSpecifyCache | Similar to MmMapLockedPages but preferred in Windows 2000 and later systems. |
MmPrepareMdlForReuse | Reinitializes an MDL. |
MmProbeAndLockPages | Locks pages after verifying address validity. |
MmSizeOfMdl | Determines how much memory would be needed to create an MDL to describe a given virtual buffer. You don’t need to call this routine if you use IoAllocateMdl to create the MDL in the first place. |
MmUnlockPages | Unlocks the pages for this MDL. |
MmUnmapLockedPages | Undoes a previous MmMapLockedPages. |
Bạn có thể hình dung rằng trình quản lý vào/ra thực hiện đoạn code như dưới đây để thực hiện phương thức đọc hoặc ghi một cách trực tiếp.
KPROCESSOR_MODE mode; // <== either KernelMode or UserMode
PMDL mdl = IoAllocateMdl(uva, length, FALSE, TRUE, Irp);
MmProbeAndLockPages(mdl, mode,
reading ? IoWriteAccess : IoReadAccess);
<code to send and await IRP>
MmUnlockPages(mdl);
IoFreeMdl(mdl);
Đầu tiên, trình quản lý vào/ra tạo ra một MDL để mô tả vùng đệm của người sử dụng. Đối số thứ 3 ở đây IOAllocateMdl (False) chỉ ra rằng đây là vùng đệm dữ liệu khoá. Đối số thứ 4 (True) chỉ ra rằng trình quản lý bộ nhớ nên quản lý (trông nom) các tiến trình đã được giao (được phân công). Đối số cuối cùng (Irp) chỉ ra Irp để IDM có thể được attact. Thực chất, IOAllocateMdl thiết lập Irp→MdlAdress cho địa chỉ của MDL mới được thành lập, đó chính là cách mà bạn tìm thấy nó và cách mà trình quản lý vào ra cuối cùngcũng tìm ra nó cho dù đã xoá.