reactos操作系统实现(87)
应用程序对设备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
检查是否用户模式调用,如果是就需要使用SEH机制。 #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} (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |