windows驱动开发-I/O请求(一)

I/O请求是内核中非常重要的部分,所有的驱动功能都使用I/O请求来交互,故理解了I/O请求也就理解了驱动的工作原理。

DeviceIoControl

这个函数主要就是用于发送I/O请求:

复制代码
BOOL DeviceIoControl
(
  HANDLE hDevice,           // CreateFile返回的设备句柄
  DWORD dwIoControlCode,    // 应用程序调用驱动程序的控制命令,一般是IOCTL_XXX IOCTLs
  LPVOID lpInBuffer,        // 应用程序传递给驱动程序的数据缓冲区地址
  DWORD nInBufferSize,      // 应用程序传递给驱动程序的数据缓冲区大小,字节数
  LPVOID lpOutBuffer,       // 驱动程序返回给应用程序的数据缓冲区地址
  DWORD nOutBufferSize,     // 驱动程序返回给应用程序的数据缓冲区大小,字节数
  LPDWORD lpBytesReturned,  // 驱动程序实际返回给应用程序的数据字节数地址
  LPOVERLAPPED lpOverlapped // 重叠操作结构
);

使用这个函数的前提是,我们在驱动中定义IOCTL:

复制代码
#define USB_IOCTL_SET_CONFIGURATION CTL_CODE(FILE_DEVICE_UNKNOWN,\
0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)

这个CTL_CODE是一个接受4个参数的宏,最终将形成一个32位的数值,用于内部指示到底是哪个IOCTL,四个参数分别是:

DeviceType : 设备类型

FunctionCode : 功能码值

TransferType : 缓冲区模式

RequiredAccess : 请求权限

值得注意的是TransferType,它一共有METHOD_BUFFERED、METHOD_IN_DIRECT、 METHOD_OUT_DIRECT、METHOD_NEITHER 这四个值,它们的含义如下:

METHOD_BUFFERED: 缓冲I/O方式,此时缓冲区由系统负责拷贝,IRP中的SystemBuffer 字段包含系统地址,UserBuffer指向用户分配的地址,注意这种模式下使用SystemBuffer 即可传递数据;系统会进行负责拷贝,32位应用程序和64位驱动通讯时候,使用这个可以避免缓冲区出错。

METHOD_IN_DIRECT or METHOD_OUT_DIRECT: 直接I/O方式,此时输入/输出缓冲区会被锁定,MdlAddress 的值有效,SystemBuffer和UserBuffer无效;

METHOD_NEITHER : 自定义I/O处理,这种方案下仅有UserBuffer 有效,这个数据即为用户分配的缓冲区。

注意: 对大部分情况来说,METHOD_BUFFERED属于牺牲性能换取安全性的做法,剩下两种做法都有一定的危险性,当然,在某些情况下,使用METHOD_NEITHER是个非常不错的方案,笔者在某些性能要求苛刻的场景下使用过METHOD_NEITHER。

驱动使用IOCTL和应用层通讯的步骤如下:

1. 驱动使用定义CTL_CODE定义IOCTL;

2. 驱动使用IoCreateSymbolicLink 创建符号对象;

3. 驱动在DriverDispatch中增加对定义的IOCTL的响应,并定义输入输出缓冲区的结构;

4. 应用层引用驱动创建的IOCTL和输入输出缓冲区结构;

5. 应用层调用CreateFile打开驱动创建的符号链接,可以使用winobj.exe查看有哪些符号链接;

6. 应用层初始化输入输出结构,通过DeviceIoControl发起IRP'

7. 驱动处理IRP;

8. 应用层调用CloseHandle关闭符号链接;

IRP

IRP的结构如下(注: 在windows内核代码解析中我们会看到真正IRP结构):

复制代码
// IRP
typedef struct _IRP 
{

  PMDL  MdlAddress;    // 指向MDL地址
  ULONG  Flags;        // 文件系统驱动程序使用此字段,该字段对所有驱动程序都是只读的。
  union 
  {
    struct _IRP  *MasterIrp; // 指向IRP中主IRP的指针
    PVOID  SystemBuffer;     // 指向系统空间缓冲区的指针,根据I/O请求不同也不同
  } AssociatedIrp;

  IO_STATUS_BLOCK  IoStatus;         // 驱动程序在调用IoCompleteRequest之前存储状态和信息
  //char             CurrentLocation;  // 当前的栈单元
  KPROCESSOR_MODE  RequestorMode;    // 指示操作的原始请求者的执行模式
  BOOLEAN PendingReturned;           // 如果设置为TRUE,则驱动程序已将IRP标记为挂起。
    // 每个IoCompletion例程都应该检查该标志的值。如果标志为TRUE,并且IoCompletion例程不会返回 
    // STATUS_MORE_PROCESSSING_REQUIRED,则该例程应调用IoMarkIrpPending,
    // 将挂起状态传播到设备堆栈中其上方的驱动程序。
  BOOLEAN  Cancel;    // 是否取消的标志位
  KIRQL  CancelIrql;  // 调用IoAcquireCancelSpinLock时驱动程序运行的IRQL
  PDRIVER_CANCEL  CancelRoutine;    // 取消例程毁掉
  PVOID UserBuffer;                 // 功能代码是IRP_MJ_DEVICE_CONTROL,并且I/O控制代码    
                                    // METHOD_NEITHER定义的,则包含输出缓冲区的地址。
  union 
  {
    struct 
    {
        union 
        {
          KDEVICE_QUEUE_ENTRY DeviceQueueEntry; // IRP排队时,指向排队的IRP
          struct 
          {
            PVOID  DriverContext[4]; // 如果IRP没有排队,那么驱动可以使用此字段来存储最多
                                     // 四个指针。此字段只能在驱动程序拥有IRP时使用。
          };
        };
    PETHREAD  Thread;        // 指向调用方的线程控制块的指针
    LIST_ENTRY  ListEntry;   // 如果驱动管理自己的IRP队列,会使用此字段将一个IRP链接到下一个

    // 被屏蔽的结构成员
    // struct IO_STACK_LOCATION *CurrentStackLocation;    // 指向当前栈单元
    } Overlay;
  } Tail;
} IRP, *PIRP;

实际上,这个结构并不是真正的IRP结构,它更多的是在操作系统我们可以看到的结构,在里面有个结构被省略了,就是CurrentStackLocation,它实际上指向当前正在处理的栈单元。驱动例程中的派发例程如下:

复制代码
DRIVER_DISPATCH DriverDispatch;

NTSTATUS DriverDispatch(
  [in, out] _DEVICE_OBJECT *DeviceObject,
  [in, out] _IRP *Irp
);

我们可以看到,例程的参数之一就是IRP结构,我们可以使用IoGetCurrentIrpStackLocation函数获取当前的栈单元PIO_STACK_LOCATION,这个结构定义如下:

复制代码
typedef struct _IO_STACK_LOCATION {
  UCHAR  MajorFunction;    // 主功能码
  UCHAR  MinorFunction;    // 子功能码
  UCHAR  Flags;            // 由文件系统驱动程序使用。
  UCHAR  Control;
  union {
        //
        // Parameters for IRP_MJ_CREATE 
        //
        struct {
            PIO_SECURITY_CONTEXT  SecurityContext;
            ULONG  Options;
            USHORT POINTER_ALIGNMENT  FileAttributes;
            USHORT  ShareAccess;
            ULONG POINTER_ALIGNMENT  EaLength;
        } Create;
        //
        // Parameters for IRP_MJ_READ 
        //
        struct {
            ULONG  Length;
            ULONG POINTER_ALIGNMENT  Key;
            LARGE_INTEGER  ByteOffset;
        } Read;
        //
        // Parameters for IRP_MJ_WRITE 
        //
        struct {
            ULONG  Length;
            ULONG POINTER_ALIGNMENT  Key;
            LARGE_INTEGER  ByteOffset;
        } Write;
        //
        // Parameters for IRP_MJ_QUERY_INFORMATION 
        //
        struct {
            ULONG  Length;
            FILE_INFORMATION_CLASS POINTER_ALIGNMENT  FileInformationClass;
        } QueryFile;
        //
        // Parameters for IRP_MJ_SET_INFORMATION 
        //
        struct {
            ULONG  Length;
            FILE_INFORMATION_CLASS POINTER_ALIGNMENT  FileInformationClass;
            PFILE_OBJECT  FileObject;
            union {
                struct {
                    BOOLEAN  ReplaceIfExists;
                    BOOLEAN  AdvanceOnly;
                };
                ULONG  ClusterCount;
                HANDLE  DeleteHandle;
            };
        } SetFile;
        //
        // Parameters for IRP_MJ_QUERY_VOLUME_INFORMATION 
        //
        struct {
            ULONG  Length;
            FS_INFORMATION_CLASS POINTER_ALIGNMENT  FsInformationClass;
        } QueryVolume;
        //
        // Parameters for IRP_MJ_DEVICE_CONTROL and IRP_MJ_INTERNAL_DEVICE_CONTROL 
        //
        struct {
            ULONG  OutputBufferLength;
            ULONG POINTER_ALIGNMENT  InputBufferLength;
            ULONG POINTER_ALIGNMENT  IoControlCode;
            PVOID  Type3InputBuffer;
        } DeviceIoControl;
        //
        // Nonsystem service parameters.
        //
        // Parameters for IRP_MN_MOUNT_VOLUME 
        //
        struct {
            PVOID  DoNotUse1;
            PDEVICE_OBJECT  DeviceObject;
        } MountVolume;
        //
        // Parameters for IRP_MN_VERIFY_VOLUME 
        //
        struct {
            PVOID  DoNotUse1;
            PDEVICE_OBJECT  DeviceObject;
        } VerifyVolume;
        //
        // Parameters for Scsi using IRP_MJ_INTERNAL_DEVICE_CONTROL 
        //
        struct {
            struct _SCSI_REQUEST_BLOCK  *Srb;
        } Scsi;
        //
        // Parameters for IRP_MN_QUERY_DEVICE_RELATIONS 
        //
        struct {
            DEVICE_RELATION_TYPE  Type;
        } QueryDeviceRelations;
        //
        // Parameters for IRP_MN_QUERY_INTERFACE 
        //
        struct {
            CONST GUID  *InterfaceType;
            USHORT  Size;
            USHORT  Version;
            PINTERFACE  Interface;
            PVOID  InterfaceSpecificData;
        } QueryInterface;
        //
        // Parameters for IRP_MN_QUERY_CAPABILITIES 
        //
        struct {
            PDEVICE_CAPABILITIES  Capabilities;
        } DeviceCapabilities;
        //
        // Parameters for IRP_MN_FILTER_RESOURCE_REQUIREMENTS 
        //
        struct {
            PIO_RESOURCE_REQUIREMENTS_LIST  IoResourceRequirementList;
        } FilterResourceRequirements;
        //
        // Parameters for IRP_MN_READ_CONFIG and IRP_MN_WRITE_CONFIG 
        //
        struct {
            ULONG  WhichSpace;
            PVOID  Buffer;
            ULONG  Offset;
            ULONG  POINTER_ALIGNMENT Length;
        } ReadWriteConfig;
        //
        // Parameters for IRP_MN_SET_LOCK 
        //
        struct {
            BOOLEAN  Lock;
        } SetLock;
        //
        // Parameters for IRP_MN_QUERY_ID 
        //
        struct {
            BUS_QUERY_ID_TYPE  IdType;
        } QueryId;
        //
        // Parameters for IRP_MN_QUERY_DEVICE_TEXT 
        //
        struct {
            DEVICE_TEXT_TYPE  DeviceTextType;
            LCID POINTER_ALIGNMENT  LocaleId;
        } QueryDeviceText;
        //
        // Parameters for IRP_MN_DEVICE_USAGE_NOTIFICATION 
        //
        struct {
            BOOLEAN  InPath;
            BOOLEAN  Reserved[3];
            DEVICE_USAGE_NOTIFICATION_TYPE POINTER_ALIGNMENT Type;
        } UsageNotification;
        //
        // Parameters for IRP_MN_WAIT_WAKE 
        //
        struct {
            SYSTEM_POWER_STATE  PowerState;
        } WaitWake;
        //
        // Parameter for IRP_MN_POWER_SEQUENCE 
        //
        struct {
            PPOWER_SEQUENCE  PowerSequence;
        } PowerSequence;
        //
        // Parameters for IRP_MN_SET_POWER and IRP_MN_QUERY_POWER 
        //
        struct {
            ULONG  SystemContext;
            POWER_STATE_TYPE POINTER_ALIGNMENT  Type;
            POWER_STATE POINTER_ALIGNMENT  State;
            POWER_ACTION POINTER_ALIGNMENT  ShutdownType;
        } Power;
        //
        // Parameters for IRP_MN_START_DEVICE 
        //
        struct {
            PCM_RESOURCE_LIST  AllocatedResources;
            PCM_RESOURCE_LIST  AllocatedResourcesTranslated;
        } StartDevice;
        //
        // Parameters for WMI Minor IRPs 
        //
        struct {
            ULONG_PTR  ProviderId;
            PVOID  DataPath;
            ULONG  BufferSize;
            PVOID  Buffer;
        } WMI;
        //
        // Others - driver-specific
        //
        struct {
            PVOID  Argument1;
            PVOID  Argument2;
            PVOID  Argument3;
            PVOID  Argument4;
        } Others;
    } Parameters;
  PDEVICE_OBJECT  DeviceObject;    // 指向驱动程序创建的DEVICE_OBECT结构的指针
  PFILE_OBJECT  FileObject;        // 指向设备对象关联的FILE_OBJECT结构的指针
} IO_STACK_LOCATION, *PIO_STACK_LOCATION;

注意PIO_STACK_LOCATION结构的成员有效性取决于PIO_STACK_LOCATION中的MajorFunction以及MinorFunction。

相关推荐
h39744 小时前
DirectShow过滤器开发-视频源过滤器
c++·windows·音视频·directshow
java资料站11 小时前
二、Spring AI Alibaba · ChatModel
java·windows·spring
北冥you鱼12 小时前
Go 语言空接口(interface{})使用场景与最佳实践
开发语言·windows·golang
2401_8628808213 小时前
iMX6ULL 驱动工程搭建 + LED 灯驱动
c语言·驱动开发·嵌入式硬件
夏虫不可与之言冰14 小时前
Windows WSL2 (Ubuntu) 搭建 ESP‑IDF 6.1 开发环境全过程演示
linux·windows·ubuntu·esp32·乐鑫科技·乐鑫eim·乐鑫代理商
打工仔折腾 AI17 小时前
FaceFusion本地换脸实战:Windows整合包、模型选择与遮罩调参记录
人工智能·windows·后端·python·深度学习·性能优化·ai agent 实战
硅基手札18 小时前
【串口技术系列文档 07】UART控制器寄存器详解
驱动开发·stm32·单片机·嵌入式硬件·mcu·计算机外设
硅基手札18 小时前
【串口技术系列文档 09】FIFO管理与流控
驱动开发·stm32·单片机·嵌入式硬件·mcu·计算机外设
硅基手札18 小时前
【串口技术系列文档 08】中断驱动vs轮询vs DMA
驱动开发·stm32·单片机·嵌入式硬件·mcu·计算机外设
小蜗牛狂飙记19 小时前
Windows 双击 Jar 包无反应,不用写 bat 脚本!Jarfix 一键修复(以 Struts2 扫描工具为例)
windows·struts·jar