应用程序对设备I/O进行Win32调用,这个调用由I/O系统服务接收,然后I/O管理器从这个请求构造一个合适的I/O请求包(IRP)。那么I/O管理器是怎么样创建这个I/O请求包(IRP)的呢?又是怎么样传送给驱动程序的呢?我们带着这两个问题来分析下面实现文件读取的代码,如下:
#001 NTSTATUS
#002 NTAPI
#003 NtReadFile(IN HANDLE FileHandle,
#004 IN HANDLE Event OPTIONAL,
#005 IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
#006 IN PVOID ApcContext OPTIONAL,
#007 OUT PIO_STATUS_BLOCK IoStatusBlock,
#008 OUT PVOID Buffer,
#009 IN ULONG Length,
#010 IN PLARGE_INTEGER ByteOffset OPTIONAL,
#011 IN PULONG Key OPTIONAL)
#012 {
#013 NTSTATUS Status = STATUS_SUCCESS;
#014 PFILE_OBJECT FileObject;
#015 PIRP Irp;
#016 PDEVICE_OBJECT DeviceObject;
#017 PIO_STACK_LOCATION StackPtr;
#018 KPROCESSOR_MODE PrevIoUsMode = KeGetPrevIoUsMode();
#019 PKEVENT EventObject = NULL;
#020 LARGE_INTEGER CapturedByteOffset;
#021 ULONG CapturedKey = 0;
#022 BOOLEAN Synchronous = FALSE;
#023 PMDL Mdl;
#024 PAGED_CODE();
#025 CapturedByteOffset.QuadPart = 0;
#026 IOTRACE(IO_API_DEBUG,"FileHandle: %p/n",FileHandle);
#027
#028 /* Validate User-Mode Buffers */
#029 if(PrevIoUsMode != KernelMode)
#030 {
使用SEH机制,以便截取异常。
#031 _SEH2_TRY
#032 {
检测状态块。
#033 /* Probe the status block */
#034 ProbeForWriteIoStatusBlock(IoStatusBlock);
#035
检查读取缓冲区。
#036 /* Probe the read buffer */
#037 ProbeForWrite(Buffer,Length,1);
#038
#039 /* Check if we got a byte offset */
#040 if (ByteOffset)
#041 {
#042 /* Capture and probe it */
#043 CapturedByteOffset = ProbeForReadLargeInteger(ByteOffset);
#044 }
#045
#046 /* Capture and probe the key */
#047 if (Key) CapturedKey = ProbeForReadUlong(Key);
#048 }
#049 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
#050 {
#051 /* Get the exception code */
#052 Status = _SEH2_GetExceptionCode();
#053 }
#054 _SEH2_END;
#055
#056 /* Check for probe failure */
#057 if (!NT_SUCCESS(Status)) return Status;
#058 }
#059 else
#060 {
#061 /* Kernel mode: capture directly */
#062 if (ByteOffset) CapturedByteOffset = *ByteOffset;
#063 if (Key) CapturedKey = *Key;
#064 }
#065
#066 /* Get File Object */
#067 Status = ObReferenceObjectByHandle(FileHandle,
#068 FILE_READ_DATA,
#069 IoFileObjectType,
#070 PrevIoUsMode,
#071 (PVOID*)&FileObject,
#072 NULL);
#073 if (!NT_SUCCESS(Status)) return Status;
#074
检查事件是否响应。
#075 /* Check for event */
#076 if (Event)
#077 {
#078 /* Reference it */
#079 Status = ObReferenceObjectByHandle(Event,
#080 EVENT_MODIFY_STATE,
#081 ExEventObjectType,
#082 PrevIoUsMode,
#083 (PVOID*)&EventObject,
#084 NULL);
#085 if (!NT_SUCCESS(Status))
#086 {
#087 /* Fail */
#088 ObDereferenceObject(FileObject);
#089 return Status;
#090 }
#091
#092 /* Otherwise reset the event */
#093 KeClearEvent(EventObject);
#094 }
#095
检查是否使用同步I/O的方式。
#096 /* Check if we should use Sync IO or not */
#097 if (FileObject->Flags & FO_SYNCHRONOUS_IO)
#098 {
这里是使用同步模式。
#099 /* Lock the file object */
#100 IopLockFileObject(FileObject);
#101
#102 /* Check if we don't have a byte offset avilable */
#103 if (!(ByteOffset) ||
#104 ((CapturedByteOffset.u.LowPart == FILE_USE_FILE_POINTER_POSITION) &&
#105 (CapturedByteOffset.u.HighPart == -1)))
#106 {
#107 /* Use the Current Byte Offset instead */
#108 CapturedByteOffset = FileObject->CurrentByteOffset;
#109 }
#110
#111 /* Remember we are sync */
#112 Synchronous = TRUE;
#113 }
#114 else if (!(ByteOffset) &&
#115 !(FileObject->Flags & (FO_NAMED_PIPE | FO_MAILSLOT)))
#116 {
非法文件对象。
#117 /* Otherwise,this was async I/O without a byte offset,so fail */
#118 if (EventObject) ObDereferenceObject(EventObject);
#119 ObDereferenceObject(FileObject);
#120 return STATUS_INVALID_PARAMETER;
#121 }
#122
#123 /* Get the device object */
#124 DeviceObject = IoGetRelatedDeviceObject(FileObject);
#125
#126 /* Clear the File Object's event */
#127 KeClearEvent(&FileObject->Event);
#128
分配一个读取文件的请求包(IRP)。
#129 /* Allocate the IRP */
#130 Irp = IoAllocateIrp(DeviceObject->StackSize,FALSE);
#131 if (!Irp) return IopCleanupFailedIrp(FileObject,NULL,NULL);
#132
设置IRP的属性。
#133 /* Set the IRP */
#134 Irp->Tail.Overlay.OriginalFileObject = FileObject;
#135 Irp->Tail.Overlay.Thread = PsGetCurrentThread();
#136 Irp->RequestorMode = PrevIoUsMode;
#137 Irp->Overlay.AsynchronousParameters.UserApcRoutine = ApcRoutine;
#138 Irp->Overlay.AsynchronousParameters.UserApcContext = ApcContext;
#139 Irp->UserIosb = IoStatusBlock;
#140 Irp->UserEvent = EventObject;
#141 Irp->PendingReturned = FALSE;
#142 Irp->Cancel = FALSE;
#143 Irp->CancelRoutine = NULL;
#144 Irp->AssociatedIrp.SystemBuffer = NULL;
#145 Irp->MdlAddress = NULL;
#146
设置IRP的调用栈。
#147 /* Set the Stack Data */
#148 StackPtr = IoGetNextIrpStackLocation(Irp);
#149 StackPtr->MajorFunction = IRP_MJ_READ;
#150 StackPtr->FileObject = FileObject;
#151 StackPtr->Parameters.Read.Key = CapturedKey;
#152 StackPtr->Parameters.Read.Length = Length;
#153 StackPtr->Parameters.Read.ByteOffset = CapturedByteOffset;
#154
检查设备对象是否使用缓冲I/O的方式,还是使用直接I/O的方式。
#155 /* Check if this is buffered I/O */
#156 if (DeviceObject->Flags & DO_BUFFERED_IO)
#157 {
使用缓冲I/O的方式,就分配非分页内存。
#158 /* Check if we have a buffer length */
#159 if (Length)
#160 {
#161 /* Enter SEH */
#162 _SEH2_TRY
#163 {
#164 /* Allocate a buffer */
#165 Irp->AssociatedIrp.SystemBuffer =
#166 ExAllocatePoolWithTag(NonPagedPool,
#167 Length,
#168 TAG_SYSB);
#169 }
#170 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
#171 {
#172 /* Allocating Failed,clean up */
#173 IopCleanupAfterException(FileObject,Irp,EventObject,NULL);
#174 Status = _SEH2_GetExceptionCode();
#175 }
#176 _SEH2_END;
#177 if (!NT_SUCCESS(Status)) return Status;
#178
#179 /* Set the buffer and flags */
#180 Irp->UserBuffer = Buffer;
#181 Irp->Flags = (IRP_BUFFERED_IO |
#182 IRP_DEALLOCATE_BUFFER |
#183 IRP_INPUT_OPERATION);
#184 }
#185 else
#186 {
#187 /* Not reading anything */
#188 Irp->Flags = IRP_BUFFERED_IO | IRP_INPUT_OPERATION;
#189 }
#190 }
#191 else if (DeviceObject->Flags & DO_DIRECT_IO)
#192 {
使用直接I/O的方式,就创建内存描述符列表。
#193 /* Check if we have a buffer length */
#194 if (Length)
#195 {
#196 _SEH2_TRY
#197 {
#198 /* Allocate an MDL */
#199 Mdl = IoAllocateMdl(Buffer,FALSE,TRUE,Irp);
#200 MmProbeAndLockPages(Mdl,PrevIoUsMode,IoWriteAccess);
#201 }
#202 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
#203 {
#204 /* Allocating Failed,clean up */
#205 IopCleanupAfterException(FileObject,NULL);
#206 Status = _SEH2_GetExceptionCode();
#207 _SEH2_YIELD(return Status);
#208 }
#209 _SEH2_END;
#210
#211 }
#212
#213 /* No allocation flags */
#214 Irp->Flags = 0;
#215 }
#216 else
#217 {
#218 /* No allocation flags,and use the buffer directly */
#219 Irp->Flags = 0;
#220 Irp->UserBuffer = Buffer;
#221 }
#222
设置读取的标志。
#223 /* Now set the deferred read flags */
#224 Irp->Flags |= (IRP_READ_OPERATION | IRP_DEFER_IO_COMPLETION);
#225 #if 0
#226 /* FIXME: VFAT SUCKS */
#227 if (FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING) Irp->Flags |= IRP_NOCACHE;
#228 #endif
#229
调用函数IopPerformSynchronousRequest把IRP发送给过滤驱动程序,或者目标驱动程序。
#230 /* Perform the call */
#231 return IopPerformSynchronousRequest(DeviceObject,
#232 Irp,
#233 FileObject,
#234 TRUE,
#235 PrevIoUsMode,
#236 Synchronous,
#237 IopReadTransfer);
#238 }
#239
下面继续分析写文件的操作函数,就可以知道写文件时怎么样发送I/O请求包,实现代码如下:
#001 NTSTATUS
#002 NTAPI
#003 NtWriteFile(IN HANDLE FileHandle,
#008 IN PVOID Buffer,
#009 IN ULONG Length,
#011 IN PULONG Key OPTIONAL)
#012 {
#013 NTSTATUS Status = STATUS_SUCCESS;
#014 PFILE_OBJECT FileObject;
#015 PIRP Irp;
#016 PDEVICE_OBJECT DeviceObject;
#017 PIO_STACK_LOCATION StackPtr;
获取前一个内核模式。
#018 KPROCESSOR_MODE PrevIoUsMode = KeGetPrevIoUsMode();
#019 PKEVENT EventObject = NULL;
#020 LARGE_INTEGER CapturedByteOffset;
#021 ULONG CapturedKey = 0;
#022 BOOLEAN Synchronous = FALSE;
#023 PMDL Mdl;
#024 OBJECT_HANDLE_INFORMATION ObjectHandleInfo;
#025 PAGED_CODE();
#026 CapturedByteOffset.QuadPart = 0;
#027 IOTRACE(IO_API_DEBUG,FileHandle);
#028
#029 /* Get File Object */
#030 Status = ObReferenceObjectByHandle(FileHandle,
#031 0,
#032 IoFileObjectType,
#033 PrevIoUsMode,
#034 (PVOID*)&FileObject,
#035 &ObjectHandleInfo);
#036 if (!NT_SUCCESS(Status)) return Status;
#037
#038 /* Validate User-Mode Buffers */
#039 if(PrevIoUsMode != KernelMode)
#040 {
#041 _SEH2_TRY
#042 {
检查是否有写文件的权限。
#043 /*
#044 * Check if the handle has either FILE_WRITE_DATA or
#045 * FILE_APPEND_DATA granted. However,if this is a named pipe,
#046 * make sure we don't ask for FILE_APPEND_DATA as it interferes
#047 * with the FILE_CREATE_PIPE_INSTANCE access right!
#048 */
#049 if (!(ObjectHandleInfo.GrantedAccess &
#050 ((!(FileObject->Flags & FO_NAMED_PIPE) ?
#051 FILE_APPEND_DATA : 0) | FILE_WRITE_DATA)))
#052 {
#053 /* We Failed */
#054 ObDereferenceObject(FileObject);
#055 _SEH2_YIELD(return STATUS_ACCESS_DENIED);
#056 }
#057
#058 /* Probe the status block */
#059 ProbeForWriteIoStatusBlock(IoStatusBlock);
#060
#061 /* Probe the read buffer */
#062 ProbeForRead(Buffer,1);
#063
#064 /* Check if we got a byte offset */
#065 if (ByteOffset)
#066 {
#067 /* Capture and probe it */
#068 CapturedByteOffset = ProbeForReadLargeInteger(ByteOffset);
#069 }
#070
#071 /* Capture and probe the key */
#072 if (Key) CapturedKey = ProbeForReadUlong(Key);
#073 }
#074 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
#075 {
#076 /* Get the exception code */
#077 Status = _SEH2_GetExceptionCode();
#078 }
#079 _SEH2_END;
#080
#081 /* Check for probe failure */
#082 if (!NT_SUCCESS(Status)) return Status;
#083 }
#084 else
#085 {
#086 /* Kernel mode: capture directly */
#087 if (ByteOffset) CapturedByteOffset = *ByteOffset;
#088 if (Key) CapturedKey = *Key;
#089 }
#090
检查是否为追加的模式。
#091 /* Check if this is an append operation */
#092 if ((ObjectHandleInfo.GrantedAccess &
#093 (FILE_APPEND_DATA | FILE_WRITE_DATA)) == FILE_APPEND_DATA)
#094 {
#095 /* Give the drivers something to understand */
#096 CapturedByteOffset.u.LowPart = FILE_WRITE_TO_END_OF_FILE;
#097 CapturedByteOffset.u.HighPart = -1;
#098 }
#099
检查是否使用事件。
#100 /* Check for event */
#101 if (Event)
#102 {
#103 /* Reference it */
#104 Status = ObReferenceObjectByHandle(Event,
#105 EVENT_MODIFY_STATE,
#106 ExEventObjectType,
#107 PrevIoUsMode,
#108 (PVOID*)&EventObject,
#109 NULL);
#110 if (!NT_SUCCESS(Status))
#111 {
#112 /* Fail */
#113 ObDereferenceObject(FileObject);
#114 return Status;
#115 }
#116
清空事件。
#117 /* Otherwise reset the event */
#118 KeClearEvent(EventObject);
#119 }
#120
检查是否使用同步I/O的方式。
#121 /* Check if we should use Sync IO or not */
#122 if (FileObject->Flags & FO_SYNCHRONOUS_IO)
#123 {
#124 /* Lock the file object */
#125 IopLockFileObject(FileObject);
#126
#127 /* Check if we don't have a byte offset avilable */
#128 if (!(ByteOffset) ||
#129 ((CapturedByteOffset.u.LowPart == FILE_USE_FILE_POINTER_POSITION) &&
#130 (CapturedByteOffset.u.HighPart == -1)))
#131 {
#132 /* Use the Current Byte Offset instead */
#133 CapturedByteOffset = FileObject->CurrentByteOffset;
#134 }
#135
#136 /* Remember we are sync */
#137 Synchronous = TRUE;
#138 }
#139 else if (!(ByteOffset) &&
#140 !(FileObject->Flags & (FO_NAMED_PIPE | FO_MAILSLOT)))
#141 {
#142 /* Otherwise,so fail */
#143 if (EventObject) ObDereferenceObject(EventObject);
#144 ObDereferenceObject(FileObject);
#145 return STATUS_INVALID_PARAMETER;
#146 }
#147
#148 /* Get the device object */
#149 DeviceObject = IoGetRelatedDeviceObject(FileObject);
#150
#151 /* Clear the File Object's event */
#152 KeClearEvent(&FileObject->Event);
#153
分配一个请求包(IRP)。
#154 /* Allocate the IRP */
#155 Irp = IoAllocateIrp(DeviceObject->StackSize,FALSE);
#156 if (!Irp) return IopCleanupFailedIrp(FileObject,NULL);
#157
设置IRP的属性。
#158 /* Set the IRP */
#159 Irp->Tail.Overlay.OriginalFileObject = FileObject;
#160 Irp->Tail.Overlay.Thread = PsGetCurrentThread();
#161 Irp->RequestorMode = PrevIoUsMode;
#162 Irp->Overlay.AsynchronousParameters.UserApcRoutine = ApcRoutine;
#163 Irp->Overlay.AsynchronousParameters.UserApcContext = ApcContext;
#164 Irp->UserIosb = IoStatusBlock;
#165 Irp->UserEvent = EventObject;
#166 Irp->PendingReturned = FALSE;
#167 Irp->Cancel = FALSE;
#168 Irp->CancelRoutine = NULL;
#169 Irp->AssociatedIrp.SystemBuffer = NULL;
#170 Irp->MdlAddress = NULL;
#171
设置与IRP一致的栈空间。
#172 /* Set the Stack Data */
#173 StackPtr = IoGetNextIrpStackLocation(Irp);
#174 StackPtr->MajorFunction = IRP_MJ_WRITE;
#175 StackPtr->FileObject = FileObject;
#176 StackPtr->Flags = FileObject->Flags & FO_WRITE_THROUGH ?
#177 SL_WRITE_THROUGH : 0;
#178 StackPtr->Parameters.Write.Key = CapturedKey;
#179 StackPtr->Parameters.Write.Length = Length;
#180 StackPtr->Parameters.Write.ByteOffset = CapturedByteOffset;
#181
检查是否使用缓冲I/O的方式。
#182 /* Check if this is buffered I/O */
#183 if (DeviceObject->Flags & DO_BUFFERED_IO)
#184 {
#185 /* Check if we have a buffer length */
#186 if (Length)
#187 {
#188 /* Enter SEH */
#189 _SEH2_TRY
#190 {
#191 /* Allocate a buffer */
#192 Irp->AssociatedIrp.SystemBuffer =
#193 ExAllocatePoolWithTag(NonPagedPool,
#194 Length,
#195 TAG_SYSB);
#196
#197 /* Copy the data into it */
#198 RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,Buffer,Length);
#199 }
#200 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
#201 {
#202 /* Allocating Failed,clean up */
#203 IopCleanupAfterException(FileObject,NULL);
#204 Status = _SEH2_GetExceptionCode();
#205 _SEH2_YIELD(return Status);
#206 }
#207 _SEH2_END;
#208
#209 /* Set the flags */
#210 Irp->Flags = (IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER);
#211 }
#212 else
#213 {
#214 /* Not writing anything */
#215 Irp->Flags = IRP_BUFFERED_IO;
#216 }
#217 }
#218 else if (DeviceObject->Flags & DO_DIRECT_IO)
#219 {
这里使用直接I/O的模式,因此调用IoAllocateMdl来创建内存描述符表。
#220 /* Check if we have a buffer length */
#221 if (Length)
#222 {
#223 _SEH2_TRY
#224 {
#225 /* Allocate an MDL */
#226 Mdl = IoAllocateMdl(Buffer,Irp);
#227 MmProbeAndLockPages(Mdl,IoReadAccess);
#228 }
#229 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
#230 {
#231 /* Allocating Failed,clean up */
#232 IopCleanupAfterException(FileObject,NULL);
#233 Status = _SEH2_GetExceptionCode();
#234 _SEH2_YIELD(return Status);
#235 }
#236 _SEH2_END;
#237 }
#238
#239 /* No allocation flags */
#240 Irp->Flags = 0;
#241 }
#242 else
#243 {
#244 /* No allocation flags,and use the buffer directly */
#245 Irp->Flags = 0;
#246 Irp->UserBuffer = Buffer;
#247 }
#248
#249 /* Now set the deferred read flags */
#250 Irp->Flags |= (IRP_WRITE_OPERATION | IRP_DEFER_IO_COMPLETION);
#251 #if 0
#252 /* FIXME: VFAT SUCKS */
#253 if (FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING) Irp->Flags |= IRP_NOCACHE;
#254 #endif
#255
调用函数IopPerformSynchronousRequest来分IRP请求包给相应的驱动程序。
#256 /* Perform the call */
#257 return IopPerformSynchronousRequest(DeviceObject,
#258 Irp,
#259 FileObject,
#260 TRUE,
#261 PrevIoUsMode,
#262 Synchronous,
#263 IopWriteTransfer);
#264}
原文链接:https://www.f2er.com/react/308441.html