zl程序教程

您现在的位置是:首页 >  系统

当前栏目

reactos操作系统实现(87)

操作系统 实现 reactos 87
2023-09-14 09:10:38 时间

应用程序对设备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, Length, FALSE, TRUE, Irp);

#200                  MmProbeAndLockPages(Mdl, PreviousMode, IoWriteAccess);

#201              }

#202              _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)

#203              {

#204                  /* Allocating failed, clean up */

#205                  IopCleanupAfterException(FileObject, Irp, EventObject, 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 

 

调用函数IopPerformSynchronousRequestIRP发送给过滤驱动程序,或者目标驱动程序。

#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,

#004              IN HANDLE Event OPTIONAL,

#005              IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,

#006              IN PVOID ApcContext OPTIONAL,

#007              OUT PIO_STATUS_BLOCK IoStatusBlock,

#008              IN 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      OBJECT_HANDLE_INFORMATION ObjectHandleInfo;

#025      PAGED_CODE();

#026      CapturedByteOffset.QuadPart = 0;

#027      IOTRACE(IO_API_DEBUG, "FileHandle: %p/n", 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, Length, 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, this was async I/O without a byte offset, 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, 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, Irp, EventObject, 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, Length, FALSE, TRUE, Irp);

#227                  MmProbeAndLockPages(Mdl, PreviousMode, IoReadAccess);

#228              }

#229              _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)

#230              {

#231                  /* Allocating failed, clean up */

#232                  IopCleanupAfterException(FileObject, Irp, EventObject, 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  }