驱动程序进阶篇

我们学习程序设计,都是从“Hello World”开始的,驱动程序也不例外,今天我就写一个驱动版的“Hello World”来热热身,目的希望大家能对驱动程序的基本框架有所了解。
           驱动程序分为2类,一个是 Kernel(内核) 模式驱动,另一个是 Windows (用户窗口层)模式驱动,2种模式本质是相同,但细节不同,本文介绍的是内核模式驱动和驱动程序的安装、使用。
           驱动程序同普通的 EXE,DLL 一样,都属于PE文件,而且都有一个入口函数。但EXE中,入口函数是main() / WinMain() 和 Unicode 的 wmain() / wWinmain(),DLL的入口函数则可有可无,它是DllMain()。

所以驱动程序也有入口函数,而且是必须的,它是 DriverEntry(),再次提示,它是必须的,因为I/O管理器会首先调用驱动程序的DriverEntry(),它的作用就像DllMain()--完成一些初始化工作。

虽然我们有时候把 DriverEntry 比作main(),但二者在本质上不同,DriverEntry 的生命周期非常短,其作用仅是将内核文件镜像加载到系统中时进行驱动初始化,调用结束后驱动程序的其他部分依旧存在,并不随它而终止。

所以我们一般可把 DriverEntry 称为“入口函数”,而不可称为“主函数”。因此作为内核驱动来说,它没有一个明确的退出点,这应该是atexit无法在内核中实现的原因吧。

DriverEntry()一共有2个参数:

1)PDRIVER_OBJECT DriverObject,指向驱动程序对象的指针,我们操作驱动程序,全靠它,它是由 I/O 管理器传递进来的;

2)PUNICODE_STRING RegistryPath,驱动程序的服务主键,这个参数的使用并不多,但要注意,在DriverEntry()返回后,它可能
会消失,所以如果需要使用,记住先要保存下来。

DriverEntry() 的返回一个 NTSTATUS 值,它是一个 ULONG 值,具体的定义,请参见 DDK 中的 NTSTATUS.H 头文件,里边有详细
的定义。
            既然要写驱动版的“Hello World”,就需要确定如何来与驱动程序通信,常用的共享内存,共享事件,IOCTL宏,或者直接用ReadFile() 或 WriteFile() 进行读写,在本文里我就采用一种简单的、但又很常用的 IOCTL 宏,它依赖的 IRP派遣例程是IRP_MJ_DEVICE_CONTROL,Win32程序使用 DeviceIoControl() 与驱动进行通信,根据不同的IOCTL宏,输出不同的调试信息。

为了简便,我并没有使用 ReadFile() 将信息读出来,而是直接用 DbgPrint() 输出,所以需要使用 DbgView 查看,其他调试工具也可以。PS:偷懒!
            驱动程序与 I/O 管理器通信,使用的是 IRP,即 I/O 请求包。IRP 分为2部分:IRP 首部;IRP堆栈。

IRP 首部信息如下:
             IRP 首部:
                      IO_STATUS_BLOCK IoStatus                 //包含 I/O 请求的状态
                      PVOID AssociatedIrp.SystemBuffer       //  如果执行缓冲区 I/O,这个指针指向系统缓冲区
                      PMDL MdlAddress                        //  如果直接 I/O,这个指针指向用户缓冲区的存储器描述符表
                      PVOID UserBuffer                         // I/O 缓冲区的用户空间地址
             IRP堆栈:
                      UCHAR MajorFunction              //(主要类型) 指示 IRP_MJ_XXX派遣例程
                      UCHAR MinorFunction              //(IRP 的子类型) 同上,一般文件系统和 SCSI 驱动程序使用它
             union Parameters                  //MajorFunction的联合类型
             {
              struct Read                       //IRP_MJ_READ的参数
              ULONG Length
              ULONG Key
              LARGE_INTEGER ByteOffset
              struct Write                      //IRP_MJ_WRITE的参数
              ULONG Length
              ULONG Key
              LARGE_INTEGER ByteOffset
              struct DeviceIoControl            //IRP_MJ_DEVICE_CONTROL和IRP_MJ_INTERNAL_DEVICE_CONTROL的参数
              ULONG OutputBufferLength
              ULONG InputBufferLength
              ULONG IoControlCode
              PVOID Type3InputBuffer
             } 
             PDEVICE_OBJECT DeviceObject       //请求的目标设备对象的指针
             PFILE_OBJECT FileObject           //请求的目标文件对象的指针,如果有的话
            操作 IRP。对于不同的 IRP 函数,操作也是不同的:有的只操作 IRP 首部;有的只操作 IRP 堆栈;还有操作 IRP 整体,
            下面是一些常用的函数:
            IRP整体:
             名称                                        描述                                             调用者
             IoStartPacket                    发送IRP到Start I/O例程                Dispatch (派遣)
             IoCompleteRequest       表示所有的处理完成                       DpcForIsr (连接中断和注册)
             IoStartNextPacket           发送下一个IRP到Start I/O例程     DpcForIsr
             IoCallDriver                       发送IRP请求                                    Dispatch
             IoAllocateIrp                    请求另外的IRP                                 Dispatch
             IoFreeIrp                           释放驱动程序分配的IRP                 I/O Completion (I/0完成)

IRP堆栈:
            名称                                                     描述                                                        调用者
            IoGetCurrentIrpStackLocation      得到调用者堆栈的指针                           Dispatch
            IoMarkIrpPending                           为进一步的处理标记调用者I/O堆栈       Dispatch
            IoGetNextIrpStackLocation           得到下一个驱动程序的I/O堆栈的指针   Dispatch
            IoSetNextIrpStackLocation            将I/O堆栈指针压入堆栈                         Dispatc

在驱动程序中,IRP 派遣例程起着很重要的作用,每个 IRP 派遣例程,几乎都有对应的Win32函数,下面是几个常用的:
            IRP派遣例程:
            名称                                                              描述                                             调用者
            IRP_MJ_CREATE                                         请求一个句柄                              CreateFile
            IRP_MJ_CLEANUP                                      在关闭句柄时取消悬挂的IRP     CloseHandle
            IRP_MJ_CLOSE                                           关闭句柄                                      CloseHandle
            IRP_MJ_READ                                             从设备得到数据                           ReadFile
            IRP_MJ_WRITE                                            传送数据到设备                           WriteFile
            IRP_MJ_DEVICE_CONTROL                       控制操作(利用IOCTL宏)        DeviceIoControl
            IRP_MJ_INTERNAL_DEVICE_CONTROL   控制操作(只能被内核调用)         N/A
            IRP_MJ_QUERY_INFORMATION              得到文件的长度                           GetFileSize
            IRP_MJ_SET_INFORMATION                     设置文件的长度                           SetFileSize
            IRP_MJ_FLUSH_BUFFERS                          写输出缓冲区或丢弃输入缓冲区 FlushFileBuffers FlushConsoleInputBuffer PurgeComm
            IRP_MJ_SHUTDOWN                                  系统关闭                                      InitiateSystemShutdown

=================================================================================================================================
          先介绍一下开始写我们的驱动版的“Hello World”的流程,程序很简单:
          1,用 DriverEntry 驱动入口函数来将内核文件加载到系统文件中进行驱动化。

2,定义一个具有返回值的函数,来完成创建设备和相关功能等操作。

3,调用  IoCreateDevice()  API函数来创建一个设备,并返回一个设备对象。

4,调用  IoCreateSynbolicLink()  API函数来创建一个符号连接,使Win32程序可以使用驱动程序。

5,调用 RtlInitUnicodeString API 函数来初始化定义的设备名称和符号链接。

6,调用 IoCreateSymbolicLink API 函数将符号链接和设备进行绑定。

7,用 siwitch 语句来判断是否绑定成功,!NT_STATUS(Status)

8,定义一个具有返回值的函数,来完成 IRP 的派遣和完成的操作。

9,在在入口函数 DriverEntry 中注册派遣函数,设置 IRP_MJ_DEVICE_CONTROL 派遣例程 HelloWorldDispatch()和卸载例程 HelloWorldUnLoad()。如果 Win32 程序使用 DeviceIoControl(),则执行 HelloWorldDispatch() 函数。

10,调用 IoGetCurrentIrpStackLocation() 得到当前调用者的IRP指针。

11,定义一个无符号字符或无符号长整形的变量,来进行判断堆栈上的 IRP 的类型指针。

12,然后创建一个应用程序,用来和驱动进行通信,比如控制台应用程序,在_tmain()函数中编写获取文件句柄的代码。

13,调用 CreateFile 函数打开或创建一个文件,来得到这个文件的句柄。

11,取得IO控制代码,完成后使用IoCompleteRequest()完成IRP操作,如果使用ControlService()停止驱动程序,则执行HelloWorldUnLoad()函数。

10,调用IoDeleteSymbolicLink()删除符号连接。

11,调用IoDeleteDevice()删除已建立的设备 。

=================================================================================================================================

下面介绍代码循序:
         驱动入口 DriverEntry()
          //创建设备
          IoCreateDevice(DriverObject,        //驱动程序对象
                                        0,                   //扩展设备的大小,由于不需要,所以置0
                                        &DeviceNameString,   //设备名称
                                        FILE_DEVICE_UNKNOWN, //设备类型
                                        0,                   //指示设备允许的操作
                                        FALSE,               //如果为TRUE,表示只能有一个线程使用该设备,为FALSE,则没有限制
                                        &lpDeviceObject);    //返回的设备对象

//创建符号连接
           IoCreateSymbolicLink(&DeviceLinkString,   //存放符号连接的UNICODE_STRING
                                                    &DeviceNameString);  //设备名称

//注册派遣例程和卸载例程
          DriverObject->MajorFunction[IRP_MJ_CREATE]=
          DriverObject->MajorFunction[IRP_MJ_CLOSE]=
          DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=HelloWorldDispatch;
          DriverObject->DriverUnload=HelloWorldUnLoad;

IRP派遣例程HelloWorldDispatch()
          IrpStack=IoGetCurrentIrpStackLocation(pIrp);    //得到当前调用者的IRP堆栈

//获取IO控制代码,并执行指定操作,这里只是DbgPrint()
          IoControlCodes=IrpStack->Parameters.DeviceIoControl.IoControlCode;
          switch (IoControlCodes)

{
            ......
          IoCompleteRequest(pIrp,IO_NO_INCREMENT);     //完成IRP操作

卸载例程HelloWorldUnLoad()
           IoDeleteSymbolicLink(&DeviceLinkString);  //删除符号连接
           IoDeleteDevice(DriverObject->DeviceObject);   //删除设备

=================================================================================================================================
           完整代码:

  1. #ifndef __HELLOWORLD_C__

  2. #define __HELLOWORLD_C__
  3. #define DEBUGMSG

  4. #include <ntddk.h>

  5. #define DEVICE_HELLO_INDEX 0x860

  6. //2个IOCTL宏

  7. #define START_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX,METHOD_BUFFERED,FILE_ANY_ACCESS)
  8. #define STOP_HELLPWORLD  CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX+1,METHOD_BUFFERED,FILE_ANY_ACCESS)
  9. #define NT_DEVICE_NAME L"\\Device\\HelloWorld"        //设备名称

  10. #define DOS_DEVICE_NAME L"\\DosDevices\\HelloWorld"   //符号连接
  11. NTSTATUS HelloWorldDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp);

  12. VOID HelloWorldUnLoad (IN PDRIVER_OBJECT DriverObject);

  13. //驱动入口

  14. NTSTATUS DriverEntry (IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
  15. {
  16. NTSTATUS ntStatus=STATUS_SUCCESS;
  17. PDEVICE_OBJECT lpDeviceObject=NULL;       //指向设备对象的指针
  18. UNICODE_STRING DeviceNameString={0};      //设备名称
  19. UNICODE_STRING DeviceLinkString={0};      //符号连接
  20. //调试信息

  21. #ifdef DEBUGMSG
  22. DbgPrint("Starting DriverEntry()\n");
  23. #endif
  24. RtlInitUnicodeString(&DeviceNameString,NT_DEVICE_NAME);  //初始化Unicode字符串

  25. //创建设备
  26. ntStatus=IoCreateDevice(DriverObject,0,&DeviceNameString,FILE_DEVICE_UNKNOWN,0,FALSE,&lpDeviceObject);
  27. //使用NT_SUCCESS宏检测函数调用是否成功

  28. if (!NT_SUCCESS(ntStatus))
  29. {
  30. #ifdef DEBUGMSG
  31. DbgPrint("IoCreateDevice() error reports 0x%08X\n",ntStatus);
  32. #endif
  33. return ntStatus;
  34. }
  35. RtlInitUnicodeString(&DeviceLinkString,DOS_DEVICE_NAME);

  36. //创建符号连接
  37. ntStatus=IoCreateSymbolicLink(&DeviceLinkString,&DeviceNameString);
  38. if (!NT_SUCCESS(ntStatus))

  39. {
  40. #ifdef DEBUGMSG
  41. DbgPrint("IoCreateSymbolicLink() error reports 0x%08X\n",ntStatus);
  42. #endif
  43. if (lpDeviceObject)
  44. IoDeleteDevice(lpDeviceObject);
  45. return ntStatus;
  46. }
  47. //设置IRP派遣例程和卸载例程

  48. DriverObject->MajorFunction[IRP_MJ_CREATE]=
  49. DriverObject->MajorFunction[IRP_MJ_CLOSE]=
  50. DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=HelloWorldDispatch;
  51. DriverObject->DriverUnload=HelloWorldUnLoad;
  52. return ntStatus;

  53. }
  54. NTSTATUS HelloWorldDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp)

  55. {
  56. NTSTATUS ntStatus=STATUS_SUCCESS;
  57. PIO_STACK_LOCATION IrpStack=NULL;   //IRP堆栈
  58. ULONG IoControlCodes=0;             //I/O控制代码
  59. //设置IRP状态

  60. pIrp->IoStatus.Status=STATUS_SUCCESS;
  61. pIrp->IoStatus.Information=0;
  62. #ifdef DEBUGMSG

  63. DbgPrint("Starting HelloWorldDispatch()\n");
  64. #endif
  65. IrpStack=IoGetCurrentIrpStackLocation(pIrp);    //得到当前调用者的IRP

  66. switch (IrpStack->MajorFunction)

  67. {
  68. case IRP_MJ_CREATE:
  69. #ifdef DEBUGMSG
  70. DbgPrint("IRP_MJ_CREATE\n");
  71. #endif
  72. break;
  73. case IRP_MJ_CLOSE:

  74. #ifdef DEBUGMSG
  75. DbgPrint("IRP_MJ_CLOSE\n");
  76. #endif
  77. break;
  78. case IRP_MJ_DEVICE_CONTROL:

  79. #ifdef DEBUGMSG

  80. DbgPrint("IRP_MJ_DEVICE_CONTROL\n");
  81. #endif
  82. //取得I/O控制代码

  83. IoControlCodes=IrpStack->Parameters.DeviceIoControl.IoControlCode;
  84. switch (IoControlCodes)

  85. {
  86. //启动
  87. case START_HELLPWORLD:
  88. DbgPrint("Starting \"Hello World\"\n");
  89. break;
  90. //停止

  91. case STOP_HELLPWORLD:
  92. DbgPrint("Stoping \"Hello World\"\n");
  93. break;
  94. default:

  95. pIrp->IoStatus.Status=STATUS_INVALID_PARAMETER;
  96. break;
  97. }
  98. break;

  99. default:

  100. break;
  101. }
  102. ntStatus=pIrp->IoStatus.Status;

  103. IoCompleteRequest(pIrp,IO_NO_INCREMENT);
  104. return ntStatus;

  105. }
  106. VOID HelloWorldUnLoad (IN PDRIVER_OBJECT DriverObject)

  107. {
  108. UNICODE_STRING DeviceLinkString={0};
  109. PDEVICE_OBJECT DeviceObjectTemp1=NULL;
  110. PDEVICE_OBJECT DeviceObjectTemp2=NULL;
  111. #ifdef DEBUGMSG

  112. DbgPrint("Starting HelloWorldUnLoad()\n");
  113. #endif
  114. RtlInitUnicodeString(&DeviceLinkString,DOS_DEVICE_NAME);

  115. if (DeviceLinkString.Buffer)

  116. IoDeleteSymbolicLink(&DeviceLinkString);
  117. if (DriverObject)

  118. {
  119. DeviceObjectTemp1=DriverObject->DeviceObject;
  120. while (DeviceObjectTemp1)

  121. {
  122. DeviceObjectTemp2=DeviceObjectTemp1;
  123. DeviceObjectTemp1=DeviceObjectTemp1->NextDevice;
  124. IoDeleteDevice(DeviceObjectTemp2);
  125. }
  126. }
  127. }
  128. #endif


用户态程序:

  1. #define DEBUGMSG

  2. #include <windows.h>

  3. #include <winioctl.h>
  4. #include <stdio.h>
  5. #define DEVICE_FILTER_INDEX 0x860

  6. #define START_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_FILTER_INDEX,METHOD_BUFFERED,FILE_ANY_ACCESS)

  7. #define STOP_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_FILTER_INDEX+1,METHOD_BUFFERED,FILE_ANY_ACCESS)
  8. #define erron GetLastError()

  9. #define MY_DEVICE_NAME "\\\\.\\HelloWorld"

  10. #define MY_DEVICE_START "-start"

  11. #define MY_DEVICE_STOP "-stop"
  12. BOOL DriverControl (TCHAR *Maik);

  13. void Usage (TCHAR *Paramerter);

  14. int main (int argc,TCHAR *argv[])

  15. {
  16. if (argc!=2)
  17. {
  18. Usage(argv[0]);
  19. return 0;
  20. }
  21. if (strcmpi(argv[1],MY_DEVICE_START)==0 || strcmpi(argv[1],MY_DEVICE_STOP)==0)

  22. DriverControl(argv[1]);
  23. else
  24. {
  25. Usage(argv[0]);
  26. return 0;
  27. }
  28. return 0;

  29. }
  30. BOOL DriverControl (TCHAR *Maik)

  31. {
  32. HANDLE hDevice=NULL;  //设备句柄
  33. //获得设备句柄

  34. hDevice=CreateFile(MY_DEVICE_NAME,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
  35. if (hDevice==INVALID_HANDLE_VALUE)

  36. {
  37. #ifdef DEBUGMSG
  38. printf("CreateFile() GetLastError reports %d\n",erron);
  39. #endif
  40. return FALSE;
  41. }
  42. //启动

  43. if (strcmpi(Maik,MY_DEVICE_START)==0)
  44. {
  45. //传递启动的I/O控制代码
  46. if (!(DeviceIoControl(hDevice,START_HELLPWORLD,NULL,0,NULL,0,NULL,NULL)))
  47. {
  48. #ifdef DEBUGMSG
  49. printf("DeviceIoControl() GetLastError reports %d\n",erron);
  50. #endif
  51. CloseHandle(hDevice);
  52. return FALSE;
  53. }
  54. }
  55. //停止

  56. if (strcmpi(Maik,MY_DEVICE_STOP)==0)
  57. {
  58. //传递停止的I/O控制代码
  59. if (!(DeviceIoControl(hDevice,STOP_HELLPWORLD,NULL,0,NULL,0,NULL,NULL)))
  60. {
  61. #ifdef DEBUGMSG
  62. printf("DeviceIoControl() GetLastError reports %d\n",erron);
  63. #endif
  64. CloseHandle(hDevice);
  65. return FALSE;
  66. }
  67. }
  68. if (hDevice)

  69. CloseHandle(hDevice);  //关闭句柄
  70. return TRUE;

  71. }
  72. void Usage (TCHAR *Paramerter)

  73. {
  74. fprintf(stderr,"============================================================================\n"
  75. "      驱动版Hello World\n"
  76. "作者:dahubaobao[E.S.T]\n"
  77. "主页:www.eviloctal.com\n"
  78. "OICQ:382690\n\n"
  79. "%s -start\t启动\n"
  80. "%s -stop \t停止\n\n"
  81. "本程序只是用做代码交流,如有错误,还请多多包含!\n"
  82. "============================================================================\n"
  83. ,Paramerter,Paramerter);
  84. }

时间: 2024-10-08 04:53:47

驱动程序进阶篇的相关文章

java web进阶篇(四) Tomcat数据源

动态web开发的最大特点是可以进行数据库的操作,传统的jdbc操作由于步骤重复性造成程序性能下降. 先来回顾JDBC的操作原理 1.加载数据库驱动程序,数据库驱动程序通过classpath配置. 2.通过DirverManager类取得数据库连接对象. 3.通过Connection实例化PreparedStatement对象,编写sql语句命令操作数据库. 4.数据库属于资源操作,操作完成后要关闭数据库以释放资源. 其实以上操作,1.2.4步骤是重复的,保留3,实际上就是数据源产生的原因. 数据

Python之路【第十七篇】:Django【进阶篇 】

Python之路[第十七篇]:Django[进阶篇 ] Model 到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创建数据库,设计表结构和字段 使用 MySQLdb 来连接数据库,并编写数据访问层代码 业务逻辑层去调用数据访问层执行数据库操作 import MySQLdb def GetList(sql): db = MySQLdb.connect(user='root', db='wupeiqidb', passwd='1234', host='localhost')

Maya基础与建模教程 AE教程进阶篇 3DS MAX影视特效教程 Flash CS4案例教程

热门推荐电脑办公计算机基础知识教程 Excel2010基础教程 Word2010基础教程 PPT2010基础教程 五笔打字视频教程 Excel函数应用教程 Excel VBA基础教程 WPS2013表格教程 更多>平面设计PhotoshopCS5教程 CorelDRAW X5视频教程 Photoshop商业修图教程 Illustrator CS6视频教程 更多>室内设计3Dsmax2012教程 效果图实例提高教程 室内设计实战教程 欧式效果图制作实例教程 AutoCAD2014室内设计 Aut

在Horizon Workspace中配置Windows单点登录-进阶篇

在上一篇基础篇里面,简单介绍了在Horizon Workspace 1.8中如何配置Windows单点登录.在这篇博客中,会继续介绍一些针对大规模虚机或者虚拟桌面部署的配置方式.这些配置方式可以保证用模板部署出来的虚机或虚拟桌面在Workspace服务器端配置完成的情况下,不再需要用户进行手动配置就能实现HorizonWorkspace的单点登录. 1. 在模板机组策略中配置IE浏览器的设置 通过在模板虚拟机上的管理控制台中设置相应的策略,可以使后续使用模板机克隆出来的虚拟机自动使用Window

Python之路【第十七篇】:Django之【进阶篇】

Python之路[第十七篇]:Django[进阶篇 ] Model 到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创建数据库,设计表结构和字段 使用 MySQLdb 来连接数据库,并编写数据访问层代码 业务逻辑层去调用数据访问层执行数据库操作 import MySQLdb def GetList(sql): db = MySQLdb.connect(user='root', db='wupeiqidb', passwd='1234', host='localhost')

Python基础—面向对象(进阶篇)

通过上一篇博客我们已经对面向对象有所了解,下面我们先回顾一下上篇文章介绍的内容: 上篇博客地址:http://www.cnblogs.com/phennry/p/5606718.html 面向对象是一种编程方式,此编程方式的实现是基于对类和对象的使用: 类是一个模版,模板中包装了多个方法供使用(这里方法就是函数): 对象,根据模板创建的实例,实例用于调用被包装在类中的函数: 面向对象的三大特性:封装.继承.多态. 今天博客的内容主要介绍:Python类的成员.成员修饰符.类的特殊成员.异常处理和

ASP.NET MVC URL重写与优化(进阶篇)-继承RouteBase玩转URL

http://www.cnblogs.com/John-Connor/archive/2012/05/03/2478821.html 引言-- 在初级篇中,我们介绍了如何利用基于ASP.NET MVC的Web程序中的Global文件来简单的重写路由.也介绍了它本身的局限性-依赖于路由信息中的键值对: 如果键值对中没有的值,我们无法将其利用凑出我们想要的URL表达式. 初级篇传送门:使用Global路由表定制URL   在进阶篇中,我们将介绍ASP.NET 路由相关类的基类-抽象类RouteBas

Visual Studio调试之断点进阶篇

Visual Studio调试之断点进阶篇 在上一篇文章Visual Studio调试之断点基础篇里面介绍了什么是断点,INT 是Intel系列CPU的一个指令,可以让程序产生一个中断或者异常.程序中如果有中断或者异常发生了以后,CPU会中断程序的执行,去一个叫做IDT的部件查找处理这个中断(或者异常)的例程(Handler).IDT是操作系统在启动的时候初始化的,至于IDT的细节问题,例如什么是IDT,怎样编写一个IDT的例程,怎样 初始化IDT,可以去网上搜索一些资料. 总之,这里我们只要知

【VMCloud云平台】SCVMM进阶篇(一)网络虚拟化(2)

上一篇,我们讲了下最基本的SDN,就是使用CA地址的客户端如何去访问外部的网络?网络虚拟化里是否有类似"VLAN虚拟网关"的概念?还有PA跟CA到底之间具体的联系是如何实现的? 本篇涉及的网络架构由于是SCVMM进阶篇,就把SCVMM部分单独拿出来扩展,随着SCVMM进阶篇的推进,将会不断更新SCVMM架构图,图中Red.Blue两朵云分别代表租户的两个网络,Host-GW是用来做VMM网关(IP地址设置为23): 1. 上一篇中,我们创建了SC_NetWork作为主机间通信的&quo