3.2 应用层与内核的通信方法 :访问数据的I/O方式、读写驱动程序、发送I/O控制码、内存共享

来源:互联网 发布:杨辉三角java代码 编辑:程序博客网 时间:2024/05/01 13:19

3.2.1 访问数据的I/O方式:

1. 缓冲方式I/O

2. 直接方式I/O

3. 非缓冲非直接方式I/O


3.2.2 读写驱动程序

读写驱动程序,即应用程序或者上层驱动程序发送主功能码为IRP_MJ_READ和IRP_MJ_WRITE的I/O请求包(IRP)

//////////////////////////////////////////////////////////////////////////
//驱动入口函数
//////////////////////////////////////////////////////////////////////////
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
    UNICODE_STRING DeviceName,Win32Device;
    PDEVICE_OBJECT DeviceObject = NULL;
    NTSTATUS status;
    unsigned i;


    KdPrint(("[DriverEntry]\n"));


    //设备名
    RtlInitUnicodeString(&DeviceName,L"\\Device\\Demo0");


    //符号链接号
    RtlInitUnicodeString(&Win32Device,L"\\DosDevice\\Demo0");


    //填写默认的IRP处理函数
    for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
    {
        DriverObject->MajorFunction[i] = DemoDefaultHandler;
    }


    //IRP处理函数
    DriverObject->MajorFunction[IRP_MJ_CREATE] = DemoCreateClose;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = DemoCreateClose;
    DriverObject->MajorFunction[IRP_MJ_READ] = DemoReadWrite;
    DriverObject->MajorFunction[IRP_MJ_WRITE] = DemoReadWrite;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DemoDevControl;




    //驱动卸载函数
    DriverObject->DriverUnload = DemoUnload;


    //创建设备
    status = IoCreateDevice(DriverObject, 10, &DeviceName, FILE_DEVICE_UNKNOWN,
                            0, FALSE, &DeviceObject);


    if (!NT_SUCCESS(status))
    {
        return status;
    }


    if (!DeviceObject)
    {
        return STATUS_UNEXPECTED_IO_ERROR;
    }


    //初始化这个字节的内容
    memset(DeviceObject->DeviceExtension, 'A', 10);


    //设置设备的读写方式
    //DeviceObject->Flags |= DO_BUFFERED_IO;
    //DeviceObject->Flags |= DO_DIRECT_IO;


    //创建符号链接
    status = IoCreateSymbolicLink(&Win32Device, &DeviceName);


    //设备初始化完毕,可以开始工作了
    DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;


    return STATUS_SUCCESS;
}


//////////////////////////////////////////////////////////////////////////
//读写请求
//写请求的处理只是简单地向设备自定义扩展中复制数据
//读请假的处理只是简单地从设置自定义扩展中读取数据
//注意,最大支持10字节
//////////////////////////////////////////////////////////////////////////
NTSTATUS DemoReadWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
    NTSTATUS status = STATUS_SUCCESS;
    PIO_STACK_LOCATION pSP = IoGetCurrentIrpStackLocation(Irp);
    PVOID pBuffer = NULL;
    BOOLEAN bNeither = FALSE;
    ULONG uLen = 0;


    KdPrint(("[DemoReadWrite]\n"));


    if (DeviceObject->Flags & DO_BUFFERED_IO)
    {
        KdPrint(("Flags: DO_BUFFER_IO\n"));
        pBuffer = Irp->AssociatedIrp.SystemBuffer;
    } 
    else if (DeviceObject->Flags & DO_DIRECT_IO)
    {
        KdPrint(("Flag: DO_DRIECT_IO\n"));
        pBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
    } 
    else
    {
        KdPrint(("Flags: Neither\n"));
        bNeither = TRUE;
        pBuffer = Irp->UserBuffer;
    }


    switch (pSP->MajorFunction)
    {
    case IRP_MJ_READ:
        uLen = pSP->Parameters.Read.Length;
        uLen = uLen>10?10:uLen;


        KdPrint(("IRP_MJ_READ Read Len: %d\n", pSP->Parameters.Read.Length));


        if (FALSE == bNeither)
        {
            RtlCopyMemory(pBuffer, DeviceObject->DeviceExtension, uLen);
        } 
        else
        {
            //两者皆不需要验证缓冲区的有效性
            _try
            {
                ProbeForWrite(pBuffer,uLen,4);
                RtlCopyMemory(pBuffer,DeviceObject->DeviceExtension,uLen);
            }
            _except(EXCEPTION_EXECUTE_HANDLER)
            {
                KdPrint(("IRP_MJ_READ exception!\n"));
                status = STATUS_UNSUCCESSFUL;
            }
        }
        break;


    case IRP_MJ_WRITE:
        uLen = pSP->Parameters.Write.Length;
        uLen = uLen>10?10:uLen;


        KdPrint(("IRP_MJ_WRITE WRITE Len: %d\n",pSP->Parameters.Write.Length));


        if (FALSE == bNeither)
        {
            RtlCopyMemory(DeviceObject->DeviceExtension,pBuffer,uLen);
        } 
        else
        {
            _try
            {
                ProbeForRead(pBuffer,uLen,4);
                RtlCopyMemory(DeviceObject->DeviceExtension,pBuffer,uLen);
            }
            _except(EXCEPTION_EXECUTE_HANDLER)
            {
                KdPrint(("IRP_MJ_WRITE exception!\n"));


                status = STATUS_UNSUCCESSFUL;
            }
        }
        break;
    }


    Irp->IoStatus.Status = status;
    Irp->IoStatus.Information = uLen;


    IoCompleteRequest(Irp,IO_NO_INCREMENT);


    return status;


}


3.2.3 发送I/O控制码

应用程序或者上层的驱动程序可以通过发送I/O控制码和目标设备驱动程序交互,由驱动程序的IRP(主功能码为IRP_MJ_DEVICE_CONTROL)处理函数提供支持处理

I/O控制码的布局包含:设备类型(Device Type)、请求权限(RequiredAccess)、功能码(FunctionCode)、传输类型(TransferType)

设备类型:必须和打开设备的设备类型相匹配,设备类型包括FILE_DEVICE_DISK、FILE_DEVICE_UNKNOWN等

请求权限:包括FILE_ANY_ACCESS、FILE_READ_DATA和FILE_WRITE_DATA

功能码:用于区分不同的控制权限,所有小于0x800的功能保留给微软、0x800以及更大值的功能码提供给供应商使用

传输类型:指定了不同的I/O访问方式:METHOD_BUFFERED、METHOD_IN_DIRCET、METHOD_OUT_DIRECT或者METHOD_NEITHER

I/O控制码可以使用系统提供的CTL_CODE宏定义,宏如下:

#define IOCTL_DEVICE_Function CTL_CODE(DeviceType, Function, Method, Access)


//////////////////////////////////////////////////////////////////////////
//define
//////////////////////////////////////////////////////////////////////////
#define IOCTL_BUFFERED_IO\
    CTL_CODE(FILE_DEVICE_UNKNOWN,0x8000,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define IOCTL_INDIRECT_IO\
    CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_IN_DIRECT,FILE_ANY_ACCESS)
#define IOCTL_NEITHER_IO\
    CTL_CODE(FILE_DEVICE_UNKNOWN,0x802,METHOD_NEITHER,FILE_ANY_ACCESS)


//////////////////////////////////////////////////////////////////////////
//自定义控制请求
//同样也是在应用程序和设备扩展之间复制数据
//数据最多为10字节
//////////////////////////////////////////////////////////////////////////
NTSTATUS DemoDevControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
    NTSTATUS status = STATUS_SUCCESS;
    PIO_STACK_LOCATION pSP = IoGetCurrentIrpStackLocation(Irp);
    ULONG uControlCode = pSP->Parameters.DeviceIoControl.IoControlCode;
    PVOID pInBuf = NULL, pOutBuf = NULL;
    ULONG uInLen = 0, uOutLen = 0;


    KdPrint(("[DemoDevControl]\n"));


    uInLen = pSP->Parameters.DeviceIoControl.InputBufferLength;
    uInLen = uInLen>10?10:uOutLen;


    uOutLen = pSP->Parameters.DeviceIoControl.OutputBufferLength;
    uOutLen = uOutLen>10?10:uOutLen;


    KdPrint(("uInLen: %d uOutLen: %d\n",
        pSP->Parameters.DeviceIoControl.InputBufferLength,
        pSP->Parameters.DeviceIoControl.OutputBufferLength));


    switch(uControlCode)
    {
    case IOCTL_BUFFERED_IO:
        KdPrint(("IOCTL_BUFFER_IO\n"));


        pInBuf = pOutBuf = Irp->AssociatedIrp.SystemBuffer;
        
        if (uInLen)
        {
            RtlCopyMemory(DeviceObject->DeviceExtension,pInBuf,uInLen);
        }
        if (uOutLen)
        {
            RtlCopyMemory(pOutBuf,DeviceObject->DeviceExtension,uOutLen);
        }
        break;


    case IOCTL_INDIRECT_IO:
        KdPrint(("IOCTL_INDIRECT_IO\n"));


        pInBuf = Irp->AssociatedIrp.SystemBuffer;
        pOutBuf = MmGetSystemAddressForMdl(Irp->MdlAddress);


        if (uInLen)
        {
            RtlCopyMemory(DeviceObject->DeviceExtension,pInBuf,uInLen);
        }
        if (uOutLen)
        {
            RtlCopyMemory(pOutBuf,DeviceObject->DeviceExtension,uOutLen);
        }
        break;


    case IOCTL_NEITHER_IO:
        KdPrint(("IOCTL_NEITHER_IO\n"));


        pInBuf = pSP->Parameters.DeviceIoControl.Type3InputBuffer;
        pOutBuf = Irp->UserBuffer;


        _try
        {
            if (uInLen)
            {
                ProbeForRead(pInBuf,uInLen,4);
                RtlCopyMemory(DeviceObject->DeviceExtension,pInBuf,uInLen);
            }
            if (uOutLen)
            {
                ProbeForWrite(pOutBuf,uOutLen,4);
                RtlCopyMemory(pOutBuf,DeviceObject->DeviceExtension,uOutLen);
            }
        }
        _except(EXCEPTION_EXECUTE_HANDLER)
        {
            KdPrint(("exception!\n"));
            
            status = STATUS_UNSUCCESSFUL;
        }
        break;


    default:
        status = STATUS_UNSUCCESSFUL;
        break;
    }


    Irp->IoStatus.Status = status;
    Irp->IoStatus.Information = uOutLen;


    IoCompleteRequest(Irp, IO_NO_INCREMENT);


    return status;
}


3.2.4 内存共享

应用程序和驱动程序可以共享内存。共享内存有两种实现方式:应用程序分配内存,提供给驱动程序,由驱动程序映射并锁定该内存;驱动程序分配内存,然后映射到应用程序地址范围内。

后一种方式:

1. 驱动程序分配一块内核空间

2. 使用MDL描述这片内存并锁定内存,映射到用户空间

3. 将映射到用户空间的地址提交给应用程序,此后,应用程序即可使用该地址操作这片共享内存了

#define IOCTL_SHARE_MEMORY\
    CTL_CODE(FILE_DEVICE_UNKNOWN,0x803,IOCTL_SHARE_MEMORY,FILE_ANY_ACCESS)

case IOCTL_SHARE_MEMORY:
        pOutBuf = Irp->AssociatedIrp.SystemBuffer;


        //将设备扩展作为共享内存地址,大小为10字节
        pSysAddr = DeviceObject->DeviceExtension;


        pMdl = IoAllocateMdl(pSysAddr,10,FALSE,FALSE,NULL)
        if (NULL == pMdl)
        {
            KdPrint(("IOCTL_SHARE_MEMORY IoAllocateMdl Failure!\n"));


            status = STATUS_UNSUCCESSFUL;


            break;
        }


        MmBuildMdlForNonPagedPool(pMdl);


        *(ULONG*)pOutBuf = MmMapLockedPagesSpecifyCache(
            pMdl, UserMode, MmNonCached, NULL, FALSE, NormalPagePriority);
        if (NULL == *(ULONG)*pOutBuf)
        {
            KdPrint(("IOCTL_SHARE_MEMORY MmMaoLockedPagesSpecifyCache Failure\n"));


            status = STATUS_UNSUCCESSFUL;


            IoFreeMdl(pMdl);


            break;
        }


        KdPrint(("IOCTL_SHARE_MEMORY SysBuf:0x%08x UserBuf:0x%08x\n",pSysAddr,*(ULONG*)pOutBuf));
        break;




原创粉丝点击