Buffered I/O Mode & Direct I/O Mode
There are three I/O modes in Windows kernel, they are Buffer, Direct and Neither modes. Now, we'll talk about Buffered I/O, and this article will not involve Neither mode for data transfer if processing under user-thread occupied memory space, it might be dangerous!! If client application is going to read/write data to and from driver, the memory address of data source will not be directly referenced by the underlying driver. System kernel will allocate another data buffer with equivalent size in kernel. All data transferred must be copied into this area before they are to the target place. Usually, you will call ReadFile
/WriteFile
or fread
/fwrite
to make read/write request.
(허접 해석~~)윈도우 커널에서의 I/O모드는 3가지로 Buffer, Direct, Neitehr Mode가 있다. 만약 유저Application이 드라이버로 부터 또는 드라이버에 쓰거나 읽는 다고 한다면 데이터 소스의 메모리 주소는 밑단의 드라이버에 의해 바로
접근되지 않는다.
시스템 커널은 커널안에서 동일한 사이즈의 다른 데이터 버퍼를 할당해준다. 모든 데이터 교환은 반드시
쓰고 읽기 위해 목적지에 가기 전에 이 영역에 카피 되어진다. 대개 ReadFile/WriteFile 또는 Fread/Fwrite를 콜한다.
Below code segment demos the workflow in I/O handle for read request. As we can see, the routine that is registered for reading is PsdoDispatchRead
in DriverEntry
, this member routine will read data out of Driver's internal member - DataBuffer
to client application:
NTSTATUS
PsdoDispatchRead(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PVOID Buf; //Buffer provided by user program
ULONG BufLen; //Buffer length for user provided buffer
LONGLONG Offset;//Buffer Offset
PVOID DataBuf; //Buffer provided by Driver
ULONG DataLen; //Buffer length for Driver Data Buffer
ULONG ByteTransferred;
PIO_STACK_LOCATION p_IO_STK;
PDEVICE_EXTENSION p_DVCEXT;
DbgPrint("IRP_MJ_READ : Begin\r\n");
//Get I/o Stack Location & Device Extension
p_IO_STK = IoGetCurrentIrpStackLocation(Irp);
p_DVCEXT = DeviceObject->DeviceExtension;
//Get User Output Buffer & Length
BufLen = p_IO_STK->Parameters.Read.Length;
Offset = p_IO_STK->Parameters.Read.ByteOffset.QuadPart;
Buf = (PUCHAR)(Irp->AssociatedIrp.SystemBuffer) + Offset;
//Get Driver Data Buffer & Length
DataBuf = p_DVCEXT->DataBuffer;
if (DataBuf == NULL)
DataLen = 0;
else
DataLen = 1024;
IoAcquireRemoveLock(&p_DVCEXT->RemoveLock, Irp);
DbgPrint("Output Buffer Length : %d\r\n", BufLen);
DbgPrint("Driver Data Length : %d\r\n", DataLen);
//
if (BufLen <= DataLen) {
ByteTransferred = BufLen;
} else {
ByteTransferred = DataLen;
}
RtlCopyMemory(
Buf, DataBuf,
ByteTransferred);
IoReleaseRemoveLock(&p_DVCEXT->RemoveLock, Irp);
CompleteRequest(Irp, STATUS_SUCCESS, ByteTransferred);
DbgPrint("IRP_MJ_READ : End\r\n");
return STATUS_SUCCESS;
}
Below code segment demos the possible task items in workflow that can support the normal I/O requests to write data from application to driver.
NTSTATUS
PsdoDispatchWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PVOID Buf; //Buffer provided by user program
ULONG BufLen; //Buffer length for user provided buffer
LONGLONG Offset;//Buffer Offset
PVOID DataBuf; //Buffer provided by Driver
ULONG DataLen; //Buffer length for Driver Data Buffer
ULONG ByteTransferred;
PIO_STACK_LOCATION p_IO_STK;
PDEVICE_EXTENSION p_DVCEXT;
NTSTATUS status;
DbgPrint("IRP_MJ_WRITE : Begin\r\n");
//Get I/o Stack Location & Device Extension
p_IO_STK = IoGetCurrentIrpStackLocation(Irp);
p_DVCEXT = DeviceObject->DeviceExtension;
//Get User Input Buffer & Length
BufLen = p_IO_STK->Parameters.Write.Length;
Offset = p_IO_STK->Parameters.Read.ByteOffset.QuadPart;
Buf = (PUCHAR)(Irp->AssociatedIrp.SystemBuffer) + Offset;
//Get Driver Data Buffer & Length
DataBuf = p_DVCEXT->DataBuffer;
DataLen = 1024;
IoAcquireRemoveLock(&p_DVCEXT->RemoveLock, Irp);
DbgPrint("Input Buffer Length : %d\r\n", BufLen);
DbgPrint("Driver Data Length : %d\r\n", DataLen);
if (BufLen <= DataLen) {
ByteTransferred = BufLen;
} else {
ByteTransferred = DataLen;
}
ByteTransferred = BufLen;
RtlZeroMemory(
p_DVCEXT->DataBuffer,
1024);
RtlCopyMemory(
DataBuf,
Buf,
ByteTransferred);
IoReleaseRemoveLock(&p_DVCEXT->RemoveLock, Irp);
CompleteRequest(Irp, STATUS_SUCCESS, ByteTransferred);
DbgPrint("IRP_MJ_WRITE : End\r\n");
return STATUS_SUCCESS;
}
In DDK, some MMXxx
routines are provided to help you to get MDL that maps to physical address of user-provided buffer.
즉 MDL(메모리 설명자 리스트)는 User-Provided Data Buffer의 데이터를 Buffer I/O처럼 복사하지 않고 물리주소
를 직접 제공해 줌으로써 직접적인 주기억장치에 Access가 가능하게 해주는 것이다. ->(Direct I/O)
그리고 Direct I/O방식은 보통 대량의 데이터를 전송하는 DMA드라이버에만 사용된다.
Below code segment contains the statements that can support data reading under Direct I/O mode. It is achieved by Mmxxx
routine, please read it carefully, and you can also find the full code in the zip file. The most important MmXxx
you will use in this mode should be - MmGetSystemAddressForMdlSafe
, it can obtain the MDL that references the physical address of user-buffer.
NTSTATUS PsdoDispatchRead( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PVOID Buf; //Buffer provided by user program ULONG BufLen; //Buffer length for user provided buffer ULONG Offset;//Buffer Offset PVOID DataBuf; //Buffer provided by Driver ULONG DataLen; //Buffer length for Driver Data Buffer ULONG ByteTransferred; PIO_STACK_LOCATION p_IO_STK; PDEVICE_EXTENSION p_DVCEXT; DbgPrint("IRP_MJ_READ : Begin\r\n"); //Get I/o Stack Location & Device Extension p_IO_STK = IoGetCurrentIrpStackLocation(Irp); p_DVCEXT = DeviceObject->DeviceExtension; //Get User Output Buffer & Length Buf = MmGetSystemAddressForMdlSafe( Irp->MdlAddress, HighPagePriority); if (Buf == NULL) { DbgPrint("Can't get Virtual Address from MDL\r\n"); return STATUS_INSUFFICIENT_RESOURCES; } BufLen = MmGetMdlByteCount(Irp->MdlAddress); Offset = MmGetMdlByteOffset(Irp->MdlAddress); //Get Driver Data Buffer & Length DataBuf = p_DVCEXT->DataBuffer; if (DataBuf == NULL) DataLen = 0; else DataLen = 1024; IoAcquireRemoveLock(&p_DVCEXT->RemoveLock, Irp); DbgPrint("Output Buffer Length : %d\r\n", BufLen); DbgPrint("Offset for Buffer in the Memory Page: %d\r\n", Offset); DbgPrint("Driver Data Length : %d\r\n", DataLen); // if (BufLen <= DataLen) { ByteTransferred = BufLen; } else { ByteTransferred = DataLen; } RtlCopyMemory( Buf, DataBuf, ByteTransferred); IoReleaseRemoveLock(&p_DVCEXT->RemoveLock, Irp); CompleteRequest(Irp, STATUS_SUCCESS, ByteTransferred); DbgPrint("IRP_MJ_READ : End\r\n"); return STATUS_SUCCESS; }
Below code segment demos the possible workflow to write data from user application to driver:
NTSTATUS PsdoDispatchWrite( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PVOID Buf; //Buffer provided by user program ULONG BufLen; //Buffer length for user provided buffer ULONG Offset;//Buffer Offset PVOID DataBuf; //Buffer provided by Driver ULONG DataLen; //Buffer length for Driver Data Buffer ULONG ByteTransferred; PIO_STACK_LOCATION p_IO_STK; PDEVICE_EXTENSION p_DVCEXT; NTSTATUS status; DbgPrint("IRP_MJ_WRITE : Begin\r\n"); //Get I/o Stack Location & Device Extension p_IO_STK = IoGetCurrentIrpStackLocation(Irp); p_DVCEXT = DeviceObject->DeviceExtension; //Get User Input Buffer & Length Buf = MmGetSystemAddressForMdlSafe( Irp->MdlAddress, HighPagePriority); if (Buf == NULL) { DbgPrint("Can't get Virtual Address from MDL\r\n"); return STATUS_INSUFFICIENT_RESOURCES; } BufLen = MmGetMdlByteCount(Irp->MdlAddress); Offset = MmGetMdlByteOffset(Irp->MdlAddress); //Get Driver Data Buffer & Length DataBuf = p_DVCEXT->DataBuffer; DataLen = 1024; IoAcquireRemoveLock(&p_DVCEXT->RemoveLock, Irp); DbgPrint("Input Buffer Length : %d\r\n", BufLen); DbgPrint("Offset for Buffer in the Memory Page: %d\r\n", Offset); DbgPrint("Driver Data Length : %d\r\n", DataLen); if (BufLen <= DataLen) { ByteTransferred = BufLen; } else { ByteTransferred = DataLen; } ByteTransferred = BufLen; RtlZeroMemory( p_DVCEXT->DataBuffer, 1024); RtlCopyMemory( DataBuf, Buf, ByteTransferred); IoReleaseRemoveLock(&p_DVCEXT->RemoveLock, Irp); CompleteRequest(Irp, STATUS_SUCCESS, ByteTransferred); DbgPrint("IRP_MJ_WRITE : End\r\n"); return STATUS_SUCCESS; }