zoukankan      html  css  js  c++  java
  • wdk中ramdisk代码解读

    入口函数,即驱动加载函数

    NTSTATUS
    DriverEntry(
        IN PDRIVER_OBJECT DriverObject,
        IN PUNICODE_STRING RegistryPath
        )
    
    /*++
    
    Routine Description:
    
        Installable driver initialization entry point.
        This entry point is called directly by the I/O system.
    
    Arguments:
    
        DriverObject - pointer to the driver object
    
        RegistryPath - pointer to a unicode string representing the path
                       to driver-specific key in the registry
    
    Return Value:
    
        STATUS_SUCCESS if successful.
    
    --*/
    
    {
        WDF_DRIVER_CONFIG config;
    
        KdPrint(("Windows Ramdisk Driver - Driver Framework Edition.
    "));
        KdPrint(("Built %s %s
    ", __DATE__, __TIME__));
    
    
        WDF_DRIVER_CONFIG_INIT( &config, RamDiskEvtDeviceAdd );
    
        return WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE);
    }

    初始化可配置变量config,指定RamDiskEvtDeviceAdd的地址,这里的RamDiskEvtDeviceAdd相当于wdm中AddDevice回调函数,当即插即用管理器发现了一个新设备 ,则调用的函数。然后调用WdfDriverCreate返回,我的理解是WdfDriverCreate创建了WDF驱动框架。

    接下来是RamDiskEvtDeviceAdd函数

    NTSTATUS
    RamDiskEvtDeviceAdd(
        IN WDFDRIVER Driver,
        IN PWDFDEVICE_INIT DeviceInit
        )
    {
        //将要建立的设备对象的属性描述变量
        WDF_OBJECT_ATTRIBUTES   deviceAttributes;
        //将要调用的各种函数的状态返回值
        NTSTATUS                status;
        //将要建立的设备对象
        WDFDEVICE               device;
        //将要建立的队列对象的属性描述变量
        WDF_OBJECT_ATTRIBUTES   queueAttributes;
        //将要简历的队列配置变量
        WDF_IO_QUEUE_CONFIG     ioQueueConfig;
        //设备对象的扩展域的指针
        PDEVICE_EXTENSION       pDeviceExtension;
        //将要建立的队列扩展域指针
        PQUEUE_EXTENSION        pQueueContext = NULL;
        //将要建立的队列对象
        WDFQUEUE                queue;
        //初始化字符串
         DECLARE_CONST_UNICODE_STRING(ntDeviceName, NT_DEVICE_NAME);
        //保证这个函数可以操作paged内存
        PAGED_CODE();
        //防止产生警告
         UNREFERENCED_PARAMETER(Driver);
        //初始化设备的名字 
        status = WdfDeviceInitAssignName(DeviceInit, &ntDeviceName);
        if (!NT_SUCCESS(status)) {
            return status;
        }
        //磁盘设备的类型  必须是FILE_DEVICE_DISK
        WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_DISK);
        //设备的io类型   WdfDeviceIoBuffered 使用缓冲区接受数据  WdfDeviceIoDirect 直接接收数据 IRP所携带的缓冲区  可以直接使用
        //WdfDeviceIoBufferedOrDirect  前面两种方式   都可以使用
        WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);
        //Exclusive:独占    设置设备为非独占的
        WdfDeviceInitSetExclusive(DeviceInit, FALSE);
        //设置属性描述变量   就是说  设备对象的扩展  使用什么样的数据结构存储数据
        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_EXTENSION);
        //制定设备的清理回调函数
        deviceAttributes.EvtCleanupCallback = RamDiskEvtDeviceContextCleanup;
        
        //建立这个设备
        status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
        if (!NT_SUCCESS(status)) {
            return status;
        }
        //将指针指向新建立设备的设备扩展
        pDeviceExtension = DeviceGetExtension(device);
        //将队列的配置对象初始化为默认值
        WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE (
            &ioQueueConfig,
            WdfIoQueueDispatchSequential
            );
        //wdf中我们需要将发往自己创建的设备的请求处理函数  在队列对象的设置对象中设置
        
        //我们暂时只关心IoDeviceControl和读写事件
        ioQueueConfig.EvtIoDeviceControl = RamDiskEvtIoDeviceControl;
        ioQueueConfig.EvtIoRead          = RamDiskEvtIoRead;
        ioQueueConfig.EvtIoWrite         = RamDiskEvtIoWrite;
        //指定这个队列设备的属性描述对象
        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&queueAttributes, QUEUE_EXTENSION);
        
        //创建这个队列对象  这里第一个参数是设备对象   说明这个队列在创建的时候   已经和设备绑定了
        status = WdfIoQueueCreate( device,
                                   &ioQueueConfig,
                                   &queueAttributes,
                                   &queue );
        if (!NT_SUCCESS(status)) {
            return status;
        }
        //获取队列设备的扩展指针
        pQueueContext = QueueGetExtension(queue);
        //设置队列对象的扩展的设备对象扩展
        pQueueContext->DeviceExtension = pDeviceExtension;
        
        //设置进度条显示
        //status = SetForwardProgressOnQueue(queue);
        //if (!NT_SUCCESS(status)) {
        //    return status;
        //}
        
        //初始化设备扩展中一些变量
        pDeviceExtension->DiskRegInfo.DriveLetter.Buffer =
            (PWSTR) &pDeviceExtension->DriveLetterBuffer;
        pDeviceExtension->DiskRegInfo.DriveLetter.MaximumLength =
            sizeof(pDeviceExtension->DriveLetterBuffer);
        //从注册表中获取    这个注册表信息  其实是由inf文件指定
        RamDiskQueryDiskRegParameters(
            WdfDriverGetRegistryPath(WdfDeviceGetDriver(device)),
            &pDeviceExtension->DiskRegInfo
            );
        //分配非分页内存给这个磁盘  DiskImage是磁盘镜像的意思
        //分页内存和非分页内存   分页内存的存储介质有可能在内存  也有可能在硬盘
        //非分页内存的存储介质一定是内存    所以分配非分页内存  不会引起复杂的换页操作和一些缺页中断
        //RAMDISK_TAG 代表空间标识  便于调试
        pDeviceExtension->DiskImage = ExAllocatePoolWithTag(
            NonPagedPool,
            pDeviceExtension->DiskRegInfo.DiskSize,
            RAMDISK_TAG
            );
            
        if (pDeviceExtension->DiskImage) {
    
            UNICODE_STRING deviceName;
            UNICODE_STRING win32Name;
            //初始化磁盘空间
            RamDiskFormatDisk(pDeviceExtension);
    
            status = STATUS_SUCCESS;
    
    
            //    /DosDevice/xxx    windows下代表了磁盘设备
            RtlInitUnicodeString(&win32Name, DOS_DEVICE_NAME);
            RtlInitUnicodeString(&deviceName, NT_DEVICE_NAME);
    
            pDeviceExtension->SymbolicLink.Buffer = (PWSTR)
                &pDeviceExtension->DosDeviceNameBuffer;
            pDeviceExtension->SymbolicLink.MaximumLength =
                sizeof(pDeviceExtension->DosDeviceNameBuffer);
            pDeviceExtension->SymbolicLink.Length = win32Name.Length;
    
            RtlCopyUnicodeString(&pDeviceExtension->SymbolicLink, &win32Name);
            //初始化磁盘盘符
            RtlAppendUnicodeStringToString(&pDeviceExtension->SymbolicLink,
                                           &pDeviceExtension->DiskRegInfo.DriveLetter);
    
            status = WdfDeviceCreateSymbolicLink(device,
                                                 &pDeviceExtension->SymbolicLink);
        }
    
        return status;
            
    }

    调用PAGED_CODE()保证代码可以调用非分页内存,这个宏其实是判断IRQL级别不处于DISPATCH_LEVEL级别及以上,如果是,则触发断言。

    使用WdfDeviceInitAssignName初始化设备的名字,设备名和符号链接是不一样的,设备名只能在内核态被其他驱动识别。而符号链接则可以在用户态被应用程序识别。

    使用WdfDeviceInitSetIoType指定设备的IO类型,一共有三种类型可选,1.WdfDeviceIoBuffered 使用缓冲区接受数据  2.WdfDeviceIoDirect,直接接收数据,IRP所携带的缓冲区 ,可以直接使用

    3.WdfDeviceIoBufferedOrDirect 前面两种方式都会使用

    使用函数WdfDeviceInitSetExclusive设置设备为非独占

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE指定设备扩展使用什么样的数据结构存储数据,DEVICE_EXTENSION为自定义的结构体

    deviceAttributes.EvtCleanupCallback这里指定设备的清理函数为RamDiskEvtDeviceContextCleanup

    准备完毕之后使用WdfDeviceCreate创建磁盘设备,参数都是前面处理过,其中device为传出参数

    下面是创建队列对象的代码

    ioQueueConfig为队列对象的配置对象,首先通过WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE初始化为默认值,然后指定IoDeviceControl和读写事件,通过WdfIoQueueCreate创建这个队列对象

    注意的是队列对象的扩展结构里面包含了设备对象的扩展结构指针

    RamDiskQueryDiskRegParameters取得注册表数据,系统在驱动加载之前,会首先提供访问注册表的功能,这里的数据其实是安装驱动时ramdisk.inf指定

    pDeviceExtension->DiskImage这个指针是磁盘镜像,即磁盘位置的起始,然后为这个指针分配非分页内存,调用ExAllocatePoolWithTag传入第一个参数NonPagedPool。使用非分页内存的原因是,非分页内存的存储介质一定是内存,所以分配非分页内存 ,不会引起复杂的换页操作和一些缺页中断。

    如果分配内存成功,则使用RamDiskFormatDisk函数初始化磁盘空间。接下来使用了win32Name存储符号链接,最终的符号链接为/DosDevice/Z: ,在用户态会默认显示Z作为盘符

    刚开始我找到WdfControlFinishInitializing这个函数,以为可以通知用户层更新,从而显示出新建的磁盘对象,后来发现这个函数不能达到目的。在这里也求大神指教,如何不用重启,即可显示出新建的磁盘设备。

    //初始化磁盘结构的一些数据
    NTSTATUS
    RamDiskFormatDisk(
        IN PDEVICE_EXTENSION devExt
        )
    
    {
        //将分配的非分页内存的首地址  转化为DBR结构的指针
        PBOOT_SECTOR bootSector = (PBOOT_SECTOR) devExt->DiskImage;
        //指向FAT表的指针
        PUCHAR       firstFatSector;
        //根目录入口点个数
        ULONG        rootDirEntries;
        //一个簇由多少扇区组成
        ULONG        sectorsPerCluster;
        //fat文件系统类型
        USHORT       fatType;        // Type FAT 12 or 16
        //FAT表里面有多少表项
        USHORT       fatEntries;     // Number of cluster entries in FAT
        //一个FAT表需要占多少簇存储
        USHORT       fatSectorCnt;   // Number of sectors for FAT
        //第一个根目录入口点
        PDIR_ENTRY   rootDir;        // Pointer to first entry in root dir
        //保证这个函数可以操作paged内存
        PAGED_CODE();
        
        ASSERT(sizeof(BOOT_SECTOR) == 512);
        ASSERT(devExt->DiskImage != NULL);
        //清空磁盘镜像
        RtlZeroMemory(devExt->DiskImage, devExt->DiskRegInfo.DiskSize);
        //每个扇区有512个字节
        devExt->DiskGeometry.BytesPerSector = 512;
        //每个磁道有32个扇区
        devExt->DiskGeometry.SectorsPerTrack = 32;     // Using Ramdisk value
        //每个柱面有两个磁道
        devExt->DiskGeometry.TracksPerCylinder = 2;    // Using Ramdisk value
        //计算得出磁柱面数
        devExt->DiskGeometry.Cylinders.QuadPart = devExt->DiskRegInfo.DiskSize / 512 / 32 / 2;
        devExt->DiskGeometry.MediaType = RAMDISK_MEDIA_TYPE;
        KdPrint((
            "Cylinders: %ld
     TracksPerCylinder: %ld
     SectorsPerTrack: %ld
     BytesPerSector: %ld
    ",
            devExt->DiskGeometry.Cylinders.QuadPart, devExt->DiskGeometry.TracksPerCylinder,
            devExt->DiskGeometry.SectorsPerTrack, devExt->DiskGeometry.BytesPerSector
            ));
        //初始化根目录入口点个数
        rootDirEntries = devExt->DiskRegInfo.RootDirEntries;
        //一个簇由多少扇区组成
        sectorsPerCluster = devExt->DiskRegInfo.SectorsPerCluster;
        //这里  调整根目录数目的地方很疑惑   
        if (rootDirEntries & (DIR_ENTRIES_PER_SECTOR - 1)) {
    
            rootDirEntries =
                (rootDirEntries + (DIR_ENTRIES_PER_SECTOR - 1)) &
                    ~ (DIR_ENTRIES_PER_SECTOR - 1);
        }
    
        KdPrint((
            "Root dir entries: %ld
     Sectors/cluster: %ld
    ",
            rootDirEntries, sectorsPerCluster
            ));
        //硬编码写入跳转指令
        bootSector->bsJump[0] = 0xeb;
        bootSector->bsJump[1] = 0x3c;
        bootSector->bsJump[2] = 0x90;
        //oem名字
            bootSector->bsOemName[0] = 'R';
        bootSector->bsOemName[1] = 'a';
        bootSector->bsOemName[2] = 'j';
        bootSector->bsOemName[3] = 'u';
        bootSector->bsOemName[4] = 'R';
        bootSector->bsOemName[5] = 'a';
        bootSector->bsOemName[6] = 'm';
        bootSector->bsOemName[7] = ' ';
        //每个扇区有多少字节
        bootSector->bsBytesPerSec = (SHORT)devExt->DiskGeometry.BytesPerSector;
        //指定这个磁盘卷保留扇区   仅DBR这一个扇区为保留扇区
        bootSector->bsResSectors  = 1;
        //fat表一般一式两份  但这里就创建一份就可以
        bootSector->bsFATs        = 1;
        //指定根目录入口点个数
         bootSector->bsRootDirEnts = (USHORT)rootDirEntries;
         //磁盘总扇区数   磁盘大小除以扇区大小
        bootSector->bsSectors     = (USHORT)(devExt->DiskRegInfo.DiskSize /
                                             devExt->DiskGeometry.BytesPerSector);
        //磁盘介质类型
        bootSector->bsMedia       = (UCHAR)devExt->DiskGeometry.MediaType;
        //每个簇有多少个扇区
        bootSector->bsSecPerClus  = (UCHAR)sectorsPerCluster;
        //fat表项数  总扇区数-保留扇区数
        //这里很疑惑   如何得到FAT表项数
        fatEntries =
            (bootSector->bsSectors - bootSector->bsResSectors -
                bootSector->bsRootDirEnts / DIR_ENTRIES_PER_SECTOR) /
                    bootSector->bsSecPerClus + 2;
                    
                    
        //如果表项数  大于2的12次方   则使用FAT16
        if (fatEntries > 4087) {
            fatType =  16;
            //这一步的调整  我很不理解
            fatSectorCnt = (fatEntries * 2 + 511) / 512;
            fatEntries   = fatEntries + fatSectorCnt;
            fatSectorCnt = (fatEntries * 2 + 511) / 512;
        }
        else {
            fatType =  12;
            fatSectorCnt = (((fatEntries * 3 + 1) / 2) + 511) / 512;
            fatEntries   = fatEntries + fatSectorCnt;
            fatSectorCnt = (((fatEntries * 3 + 1) / 2) + 511) / 512;
        }
        //初始化FAT表所占的扇区数
        bootSector->bsFATsecs       = fatSectorCnt;
        //初始化DBR每个磁道的扇区数
        bootSector->bsSecPerTrack   = (USHORT)devExt->DiskGeometry.SectorsPerTrack;
        //初始化每个柱面的磁道数
        bootSector->bsHeads         = (USHORT)devExt->DiskGeometry.TracksPerCylinder;
        //初始化启动签名  windows要求必须是0x29或者0x28
        bootSector->bsBootSignature = 0x29;
        //卷ID  随便填写
        bootSector->bsVolumeID      = 0x12345678;
        //初始化卷标
            bootSector->bsLabel[0]  = 'R';
        bootSector->bsLabel[1]  = 'a';
        bootSector->bsLabel[2]  = 'm';
        bootSector->bsLabel[3]  = 'D';
        bootSector->bsLabel[4]  = 'i';
        bootSector->bsLabel[5]  = 's';
        bootSector->bsLabel[6]  = 'k';
        bootSector->bsLabel[7]  = ' ';
        bootSector->bsLabel[8]  = ' ';
        bootSector->bsLabel[9]  = ' ';
        bootSector->bsLabel[10] = ' ';
        //设置磁盘文件类型
        bootSector->bsFileSystemType[0] = 'F';
        bootSector->bsFileSystemType[1] = 'A';
        bootSector->bsFileSystemType[2] = 'T';
        bootSector->bsFileSystemType[3] = '1';
        bootSector->bsFileSystemType[4] = '?';
        bootSector->bsFileSystemType[5] = ' ';
        bootSector->bsFileSystemType[6] = ' ';
        bootSector->bsFileSystemType[7] = ' ';
    
        bootSector->bsFileSystemType[4] = ( fatType == 16 ) ? '6' : '2';
        //设置DBR结束标识
        bootSector->bsSig2[0] = 0x55;
        bootSector->bsSig2[1] = 0xAA;
        //初始化FAT表结构
        firstFatSector    = (PUCHAR)(bootSector + 1);
        firstFatSector[0] = (UCHAR)devExt->DiskGeometry.MediaType;
        firstFatSector[1] = 0xFF;
        firstFatSector[2] = 0xFF;
    
        if (fatType == 16) {
            firstFatSector[3] = 0xFF;
        }
        
        
         rootDir = (PDIR_ENTRY)(bootSector + 1 + fatSectorCnt);
         
        //接下来 初始化根目录入口点信息
        rootDir->deName[0] = 'M';
        rootDir->deName[1] = 'S';
        rootDir->deName[2] = '-';
        rootDir->deName[3] = 'R';
        rootDir->deName[4] = 'A';
        rootDir->deName[5] = 'M';
        rootDir->deName[6] = 'D';
        rootDir->deName[7] = 'R';
    
        //
        // Set device extension name to "IVE"
        // NOTE: Fill all 3 characters, eg. sizeof(rootDir->deExtension);
        //
        rootDir->deExtension[0] = 'I';
        rootDir->deExtension[1] = 'V';
        rootDir->deExtension[2] = 'E';
    
        rootDir->deAttributes = DIR_ATTR_VOLUME;
    
        return STATUS_SUCCESS;
    
    }

    RamDiskFormatDisk函数用来初始化硬盘空间,如果是引导盘,那么硬盘镜像的第一个结构是MBR(主引导扇区),但是ramdisk虚拟的硬盘没有引导系统的作用,所以,第一个结构是DBR,结构如下

    typedef struct  _BOOT_SECTOR
    {
        UCHAR       bsJump[3];          // x86 jmp instruction, checked by FS
        CCHAR       bsOemName[8];       // OEM name of formatter
        USHORT      bsBytesPerSec;      // Bytes per Sector
        UCHAR       bsSecPerClus;       // Sectors per Cluster
        USHORT      bsResSectors;       // Reserved Sectors
        UCHAR       bsFATs;             // Number of FATs - we always use 1
        USHORT      bsRootDirEnts;      // Number of Root Dir Entries
        USHORT      bsSectors;          // Number of Sectors
        UCHAR       bsMedia;            // Media type - we use RAMDISK_MEDIA_TYPE
        USHORT      bsFATsecs;          // Number of FAT sectors
        USHORT      bsSecPerTrack;      // Sectors per Track - we use 32
        USHORT      bsHeads;            // Number of Heads - we use 2
        ULONG       bsHiddenSecs;       // Hidden Sectors - we set to 0
        ULONG       bsHugeSectors;      // Number of Sectors if > 32 MB size
        UCHAR       bsDriveNumber;      // Drive Number - not used
        UCHAR       bsReserved1;        // Reserved
        UCHAR       bsBootSignature;    // New Format Boot Signature - 0x29
        ULONG       bsVolumeID;         // VolumeID - set to 0x12345678
        CCHAR       bsLabel[11];        // Label - set to RamDisk
        CCHAR       bsFileSystemType[8];// File System Type - FAT12 or FAT16
        CCHAR       bsReserved2[448];   // Reserved
        UCHAR       bsSig2[2];          // Originial Boot Signature - 0x55, 0xAA
    }   BOOT_SECTOR, *PBOOT_SECTOR;

    DBR结构如下图

  • 相关阅读:
    编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串。 但是要保证汉字不被截半个,如“我ABC”4,应该截为“我AB”,输入“我ABC汉DEF”,6,应该输出为“我ABC”而不是“我ABC+汉的半个”。
    Java基础——数据类型之间的转换
    Spring 事物机制总结
    Spring 3.0 注解注入详解
    Spring 注解 @Resource和@Autowired
    从jsp向servlet传送数据的两种方式
    文本输入框,只能显示内容而不能修改
    myeclipse 中项目名出现红色感叹号解决方法
    在servlet中使用split()截取以反斜杠‘’分割的字符串
    jsp页面跳转方式
  • 原文地址:https://www.cnblogs.com/zwt1234/p/4521697.html
Copyright © 2011-2022 走看看