<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_gussing是个错别字</title><subtitle type="text">享受，生活</subtitle><id>http://feed.cnblogs.com/blog/u/26313/rss</id><updated>2011-10-15T07:24:11Z</updated><author><name>gussing</name><uri>http://www.cnblogs.com/gussing/</uri></author><generator>CNBlogs BlogServer</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/gussing/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/26313/rss"/><entry><id>http://www.cnblogs.com/gussing/archive/2011/09/04/2165968.html</id><title type="text">LINE: 在windows上运行原生linux程序 (3) : bash和gcc可用，源码放出</title><summary type="text">bash和gcc都能运行了，离“可用的系统”又进了一步。今天整理了下代码，放到了google code 上，有兴趣的都可以下载下来看。要是有谁对这也感兴趣，可以在下面留言，一起来玩。如果把讨论范围缩小到x86平台，那么linux和windows的区别，至少在用户态层面的区别，比我们想象的要小很多，所以事实上如果你真想干的话，在windows上实现各类*nix特性并没有想象中那么困难。反过来说，在*nix上实现windows特性也完全能做到。这不是随口一说，前者有cygwin, coLinux，后者有wine，都是成熟的项目。我这个东西跟cygwin的区别前面已经说过很多次了，跟coLinux的</summary><published>2011-09-03T18:24:00Z</published><updated>2011-09-03T18:24:00Z</updated><author><name>gussing</name><uri>http://www.cnblogs.com/gussing/</uri></author><link rel="alternate" href="http://www.cnblogs.com/gussing/archive/2011/09/04/2165968.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/gussing/archive/2011/09/04/2165968.html"/><content type="html">&lt;p&gt;bash和gcc都能运行了，离&amp;ldquo;可用的系统&amp;rdquo;又进了一步。今天整理了下代码，放到了&lt;span style="color: #ff0000;"&gt;&lt;a href="http://code.google.com/p/line-is-not-emulator/"&gt;&lt;span style="color: #ff0000;"&gt;google code&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; 上，有兴趣的都可以下载下来看。要是有谁对这也感兴趣，可以在下面留言，一起来玩。&lt;/p&gt;&#xD;
&lt;p&gt;如果把讨论范围缩小到x86平台，那么linux和windows的区别，至少在用户态层面的区别，比我们想象的要小很多，所以事实上如果你真想干的话，在windows上实现各类*nix特性并没有想象中那么困难。反过来说，在*nix上实现windows特性也完全能做到。这不是随口一说，前者有&lt;a href="http://cygwin.org/"&gt;cygwin&lt;/a&gt;, &lt;a href="http://colinux.org/"&gt;coLinux&lt;/a&gt;，后者有&lt;a href="http://www.winehq.org/"&gt;wine&lt;/a&gt;，都是成熟的项目。我这个东西跟cygwin的区别前面已经说过很多次了，跟coLinux的区别在于，coLinux事实上还是有一层虚拟层的，它会在windows内核里加载一个硬件虚拟层，然后在这虚拟层上跑一个*真正的*linux内核，大致的架构可以参考&lt;a href="http://www.ibm.com/developerworks/cn/linux/l-virtualization-colinux/"&gt;这篇文章&lt;/a&gt;。而LINE不需要任何虚拟层，它是在windows内核里*直接*实现linux的系统调用，（当然，这是我的最终目标，目前还是要过cygwin这一模拟层的）。考虑到linux的程序基本是在glibc之上运行的，而glibc是纯用户态的东西，与内核的交流全部通过系统调用，如果我们真的能在windows内核里把linux系统调用全部实现，那么glibc压根就不知道自己是跑在windows上还是linux上。&lt;/p&gt;&#xD;
&lt;p&gt;LINE的核心部件有两个：内核态的int 80响应函数，以及用户态的elf loader。目前int 80响应函数的实现非常简单，20来行汇编就搞定了，因为是第一阶段嘛，所有的东西全在用户态实现，把逻辑搬到内核的工作是下一阶段的事情。大致的代码如下：&lt;/p&gt;&#xD;
&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&#xD;
&lt;pre&gt;&lt;span style="color: #008080;"&gt; 1&lt;/span&gt; &lt;span style="color: #000000;"&gt;_InterruptHandler proc&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 2&lt;/span&gt; &lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 3&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #008000;"&gt;;&lt;/span&gt;&lt;span style="color: #008000;"&gt; Check for SYSCALL_LINEXEC_HANDLER&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 4&lt;/span&gt; &lt;span style="color: #008000;"&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;CMP&lt;/span&gt;&lt;span style="color: #000000;"&gt;     EAX, 0DEADBEEFh                    &lt;/span&gt;&lt;span style="color: #008000;"&gt;;&lt;/span&gt;&lt;span style="color: #008000;"&gt;LINE.exe运行的第一件事情就是设置用户态响应函数的地址，以便从系统调用返回的时候可以转到这里。此一次int 80的eax设置成DEADBEEF，这个值不是任何系统调用号&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 5&lt;/span&gt; &lt;span style="color: #008000;"&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;JNE&lt;/span&gt;&lt;span style="color: #000000;"&gt;     reflect_syscall&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 6&lt;/span&gt; &lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 7&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;MOV&lt;/span&gt;&lt;span style="color: #000000;"&gt;     &lt;/span&gt;&lt;span style="color: #008080;"&gt;DS:&lt;/span&gt;&lt;span style="color: #000000;"&gt;_syscallHandlerPtr, EBX    &lt;/span&gt;&lt;span style="color: #008000;"&gt;;&lt;/span&gt;&lt;span style="color: #008000;"&gt;保存用户态响应函数的地址&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 8&lt;/span&gt; &lt;span style="color: #008000;"&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 9&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;IRETD&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;10&lt;/span&gt; &lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;11&lt;/span&gt; &lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;12&lt;/span&gt; &lt;span style="color: #000000;"&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;reflect_syscall:&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;13&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;PUSH&lt;/span&gt;&lt;span style="color: #000000;"&gt;    EAX&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;14&lt;/span&gt; &lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;15&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #008000;"&gt;;&lt;/span&gt;&lt;span style="color: #008000;"&gt; simple sanity check&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;16&lt;/span&gt; &lt;span style="color: #008000;"&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;MOV&lt;/span&gt;&lt;span style="color: #000000;"&gt;     EAX, &lt;/span&gt;&lt;span style="color: #008080;"&gt;DS:&lt;/span&gt;&lt;span style="color: #000000;"&gt;_syscallHandlerPtr&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;17&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;CMP&lt;/span&gt;&lt;span style="color: #000000;"&gt;     EAX, &lt;/span&gt;&lt;span style="color: #800080;"&gt;0&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;18&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;JE&lt;/span&gt;&lt;span style="color: #000000;"&gt;      no_handler&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;19&lt;/span&gt; &lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;20&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;PUSH&lt;/span&gt;&lt;span style="color: #000000;"&gt;    EBX&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;21&lt;/span&gt; &lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;22&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;MOV&lt;/span&gt;&lt;span style="color: #000000;"&gt;     EBX, DWORD PTR [ESP+&lt;/span&gt;&lt;span style="color: #800080;"&gt;8&lt;/span&gt;&lt;span style="color: #000000;"&gt;]  &lt;/span&gt;&lt;span style="color: #008000;"&gt;;&lt;/span&gt;&lt;span style="color: #008000;"&gt; 用户态程序调用int 80时，该指令自动帮你保存的返回地址&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;23&lt;/span&gt; &lt;span style="color: #008000;"&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;MOV&lt;/span&gt;&lt;span style="color: #000000;"&gt;     DWORD PTR [ESP+&lt;/span&gt;&lt;span style="color: #800080;"&gt;8&lt;/span&gt;&lt;span style="color: #000000;"&gt;], EAX  &lt;/span&gt;&lt;span style="color: #008000;"&gt;;&lt;/span&gt;&lt;span style="color: #008000;"&gt; 把返回地址替换成我们在用户态的响应函数&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;24&lt;/span&gt; &lt;span style="color: #008000;"&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;25&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;MOV&lt;/span&gt;&lt;span style="color: #000000;"&gt;     EAX, DWORD PTR [ESP+&lt;/span&gt;&lt;span style="color: #800080;"&gt;8&lt;/span&gt;&lt;span style="color: #000000;"&gt;+&lt;/span&gt;&lt;span style="color: #800080;"&gt;12&lt;/span&gt;&lt;span style="color: #000000;"&gt;]  &lt;/span&gt;&lt;span style="color: #008000;"&gt;;&lt;/span&gt;&lt;span style="color: #008000;"&gt; &lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;26&lt;/span&gt; &lt;span style="color: #008000;"&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;SUB&lt;/span&gt;&lt;span style="color: #000000;"&gt;     EAX, &lt;/span&gt;&lt;span style="color: #800080;"&gt;4&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;27&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;MOV&lt;/span&gt;&lt;span style="color: #000000;"&gt;     DWORD PTR [ESP+&lt;/span&gt;&lt;span style="color: #800080;"&gt;8&lt;/span&gt;&lt;span style="color: #000000;"&gt;+&lt;/span&gt;&lt;span style="color: #800080;"&gt;12&lt;/span&gt;&lt;span style="color: #000000;"&gt;], EAX  &lt;/span&gt;&lt;span style="color: #008000;"&gt;;&lt;/span&gt;&lt;span style="color: #008000;"&gt; &lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;28&lt;/span&gt; &lt;span style="color: #008000;"&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;MOV&lt;/span&gt;&lt;span style="color: #000000;"&gt;     DWORD PTR [EAX], EBX         &lt;/span&gt;&lt;span style="color: #008000;"&gt;;&lt;/span&gt;&lt;span style="color: #008000;"&gt; 以上几步把旧的返回地址保存起来，用户态响应函数做完事情后，就跳转回这个地址&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;29&lt;/span&gt; &lt;span style="color: #008000;"&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;30&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;POP&lt;/span&gt;&lt;span style="color: #000000;"&gt;     EBX&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;31&lt;/span&gt; &lt;span style="color: #000000;"&gt;   &lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;32&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;JMP&lt;/span&gt;&lt;span style="color: #000000;"&gt;     exit_handler&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;33&lt;/span&gt; &lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;34&lt;/span&gt; &lt;span style="color: #000000;"&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;no_handler:&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;35&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;POP&lt;/span&gt;&lt;span style="color: #000000;"&gt;     EAX&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;36&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;PUSH&lt;/span&gt;&lt;span style="color: #000000;"&gt;    -&lt;/span&gt;&lt;span style="color: #800080;"&gt;38&lt;/span&gt;&lt;span style="color: #000000;"&gt;                         &lt;/span&gt;&lt;span style="color: #008000;"&gt;;&lt;/span&gt;&lt;span style="color: #008000;"&gt; -38 == ENOSYS&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;37&lt;/span&gt; &lt;span style="color: #008000;"&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;38&lt;/span&gt; &lt;span style="color: #000000;"&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;exit_handler:&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;39&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;POP&lt;/span&gt;&lt;span style="color: #000000;"&gt;     EAX&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;40&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;IRETD&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;41&lt;/span&gt; &lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;42&lt;/span&gt; &lt;span style="color: #000000;"&gt;_InterruptHandler endp&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;43&lt;/span&gt; &lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;44&lt;/span&gt; &lt;span style="color: #000000;"&gt;End&lt;/span&gt;&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;如上所示，这段汇编把原有的调用流程全改了。原有的中断流程大概是这样的：程序调用int 80中断&amp;mdash;&amp;gt;cpu自动将EIP压栈&amp;mdash;&amp;gt;（1）进入内核做事&amp;mdash;&amp;gt;做完后内核调用iretd&amp;mdash;&amp;gt;返回用户态&amp;mdash;&amp;gt;自动把EIP恢复。改完后的中断流程大概是这样的：程序调用int 80中断&amp;mdash;&amp;gt;cpu自动将EIP压栈（我们假设为地址1）&amp;mdash;&amp;gt;进入内核&amp;mdash;&amp;gt;（2）更改栈上保留的EIP，改成用户态的响应函数（我们假设为地址2）&amp;mdash;&amp;gt;再保存地址1&amp;mdash;&amp;gt;iretd返回用户态&amp;mdash;&amp;gt;恢复EIP，此时恢复的是地址2&amp;mdash;&amp;gt;转到地址2做事情&amp;mdash;&amp;gt;（3）做完后返回地址1。对于（2）和（3）之间的事情，int 80发起程序完全不知道，它只知道中断前保存的是哪个指令，最后回来执行的还是那条指令。如果第二阶段完成后，（2）和（3）之间的东西也完全没必要存在了，它们会移到（1）这个地方。&lt;/p&gt;&#xD;
&lt;p&gt;接下来我们说elf loader。我们知道一个进程的创建过程基本是这样的：fork创建新进程&amp;mdash;&amp;gt;子进程里调用exec*函数，用目标程序替换现有程序。而替换过程基本是这样的：load elf&amp;mdash;&amp;gt;解析import table&amp;mdash;&amp;gt;加载所有依赖的库&amp;mdash;&amp;gt;调整各类库的导出函数地址&amp;mdash;&amp;gt;找到elf的入口函数地址&amp;mdash;&amp;gt;跳转。这一整套步骤在LINE里都重新实现了一遍，因为windows上只有PE loader，根本认不出elf格式的东西，所以加载elf文件的工作就得我们自己来。假设我们目前运行的是bash程序，然后敲了一个命令ls &amp;ndash;al，这个过程在linux上大概是这样的：fork&amp;mdash;&amp;gt;父进程等待，子进程exec*( &amp;ldquo;ls&amp;rdquo;, argv, env) //argv[0] = ls, argv[1] = &amp;ndash;al。在LINE上则变成了这样：fork&amp;mdash;&amp;gt;父进程等待，子进程exec*( &amp;ldquo;Line.exe&amp;rdquo;, argv, env&amp;nbsp; ) // argv[0] = line, argv[1] = ls, argv[2] = &amp;ndash;al。也就是说，原本是新开&amp;ldquo;ls &amp;ndash;al&amp;rdquo;这样一个进程，现在变成新开&amp;rdquo;line ls &amp;ndash;al&amp;rdquo;这个进程，这样所有的进程都变成由line来加载了。至于elf加载的具体细节，不管你是看linux内核源码，还是看ld-linux这个库的源码，都能有超详细的解释，这里就不多说了。&lt;/p&gt;&#xD;
&lt;p&gt;最后还有两个比较棘手的问题：内存布局和路径。不管我们怎么弄，line.exe还是一个PE文件，由windows负责加载，加载完后有一些内存块就已经被占用了（一般来说是0x40000000及以上的地址）。要是我们的elf文件需要这些地址，那事情就麻烦了。幸运的是elf的首地址一般是0x08040000，离0x40000000还远着呢，足够我们用了。但事实上即使是0x40000000以下的地址，有一些也是windows标记为不可执行的，我们必须在load elf文件之前将需要的内存块重新标记可执行。&lt;/p&gt;&#xD;
&lt;p&gt;而路径绝对是windows上最恶心人的地方（之一）。记得在之前一篇博客里我曾说过可以用windows中的namespace模拟单根的文件系统，这在第二阶段绝对是可行的，不过第一阶段因为全是在用户态实现所以会比较困难。目前我的解决方法是把所有的绝对路径都改成相对路径。比如我们的line.exe程序放在c:/line目录里，那么当linux程序访问/bin目录时，我会把当前目录附加在该目录前面，变成c:/line/bin目录。同样的/lib目录则成了c:/line/lib目录，以此类推。目前为止这套机制运行的还算不错。至于/proc，/dev这些虚拟路径，目前还没有实现，不过借助cygwin应该也还是能做。转换路径的函数如下：&lt;/p&gt;&#xD;
&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding-left:5px;padding-right:5px;"&gt;&#xD;
&lt;pre&gt;&lt;span style="color: #008080;"&gt; 1&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt;&lt;span style="color: #000000;"&gt; change_path_to_relative(&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;char&lt;/span&gt;&lt;span style="color: #000000;"&gt;*&lt;/span&gt;&lt;span style="color: #000000;"&gt; des, &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;char&lt;/span&gt;&lt;span style="color: #000000;"&gt;*&lt;/span&gt;&lt;span style="color: #000000;"&gt; src)&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 2&lt;/span&gt; &lt;span style="color: #000000;"&gt;{&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 3&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;char&lt;/span&gt;&lt;span style="color: #000000;"&gt; root_path[MAX_PATH] &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; {&lt;/span&gt;&lt;span style="color: #800080;"&gt;0&lt;/span&gt;&lt;span style="color: #000000;"&gt;};&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 4&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;char&lt;/span&gt;&lt;span style="color: #000000;"&gt;*&lt;/span&gt;&lt;span style="color: #000000;"&gt; slash &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; NULL;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 5&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;if&lt;/span&gt;&lt;span style="color: #000000;"&gt;( &lt;/span&gt;&lt;span style="color: #000000;"&gt;!&lt;/span&gt;&lt;span style="color: #000000;"&gt;src &lt;/span&gt;&lt;span style="color: #000000;"&gt;||&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #000000;"&gt;!*&lt;/span&gt;&lt;span style="color: #000000;"&gt;src &lt;/span&gt;&lt;span style="color: #000000;"&gt;||&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #000000;"&gt;!&lt;/span&gt;&lt;span style="color: #000000;"&gt;des){&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 6&lt;/span&gt; &lt;span style="color: #000000;"&gt;        &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 7&lt;/span&gt; &lt;span style="color: #000000;"&gt;    }&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 8&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;if&lt;/span&gt;&lt;span style="color: #000000;"&gt;( src[&lt;/span&gt;&lt;span style="color: #800080;"&gt;0&lt;/span&gt;&lt;span style="color: #000000;"&gt;] &lt;/span&gt;&lt;span style="color: #000000;"&gt;!=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #800000;"&gt;'&lt;/span&gt;&lt;span style="color: #800000;"&gt;/&lt;/span&gt;&lt;span style="color: #800000;"&gt;'&lt;/span&gt;&lt;span style="color: #000000;"&gt; ){&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt; 9&lt;/span&gt; &lt;span style="color: #000000;"&gt;        strcpy(des, src);&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;10&lt;/span&gt; &lt;span style="color: #000000;"&gt;        &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;11&lt;/span&gt; &lt;span style="color: #000000;"&gt;    }&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;12&lt;/span&gt; &lt;span style="color: #000000;"&gt;    strcpy(root_path, linexec_exe);       &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt;我们假设line所在的目录在启动的时候就已经保留&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;13&lt;/span&gt; &lt;span style="color: #008000;"&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;    slash &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; strrchr(root_path, &lt;/span&gt;&lt;span style="color: #800000;"&gt;'&lt;/span&gt;&lt;span style="color: #800000;"&gt;/&lt;/span&gt;&lt;span style="color: #800000;"&gt;'&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;14&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;if&lt;/span&gt;&lt;span style="color: #000000;"&gt;( &lt;/span&gt;&lt;span style="color: #000000;"&gt;!&lt;/span&gt;&lt;span style="color: #000000;"&gt;slash ){&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;15&lt;/span&gt; &lt;span style="color: #000000;"&gt;        strcpy(des, src);&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;16&lt;/span&gt; &lt;span style="color: #000000;"&gt;        &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;17&lt;/span&gt; &lt;span style="color: #000000;"&gt;    }&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;18&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #000000;"&gt;*&lt;/span&gt;&lt;span style="color: #000000;"&gt;slash &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #800000;"&gt;'&lt;/span&gt;&lt;span style="color: #800000;"&gt;\0&lt;/span&gt;&lt;span style="color: #800000;"&gt;'&lt;/span&gt;&lt;span style="color: #000000;"&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;19&lt;/span&gt; &lt;span style="color: #000000;"&gt;    strcpy(des, root_path);&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;20&lt;/span&gt; &lt;span style="color: #000000;"&gt;    strcat(des, src);&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;21&lt;/span&gt; &lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;22&lt;/span&gt; &lt;span style="color: #000000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #008080;"&gt;23&lt;/span&gt; &lt;span style="color: #000000;"&gt;}&lt;/span&gt;&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;最后附上编译步骤以及运行截图若干，一个编译好的包连上我从ReadHat 6.0上拷出来的库压缩后有200M+，等我找个免费的网络空间再上传放出。&lt;/p&gt;&#xD;
&lt;p&gt;编译驱动：&lt;/p&gt;&#xD;
&lt;p&gt;1. 下载安装WDK&lt;/p&gt;&#xD;
&lt;p&gt;2. 运行WDK编译环境，进入源代码的src目录&lt;/p&gt;&#xD;
&lt;p&gt;3. 进入int80目录&lt;/p&gt;&#xD;
&lt;p&gt;4. 运行build &amp;ndash;g &amp;ndash;c&lt;/p&gt;&#xD;
&lt;p&gt;5. 将生成的int80.sys考入i386目录&lt;/p&gt;&#xD;
&lt;p&gt;6. 用管理员权限运行install.bat&lt;/p&gt;&#xD;
&lt;p&gt;编译程序：&lt;/p&gt;&#xD;
&lt;p&gt;1. 下载安装cygwin&lt;/p&gt;&#xD;
&lt;p&gt;2. 运行cygwin，进入源代码src目录&lt;/p&gt;&#xD;
&lt;p&gt;3. 运行make&lt;/p&gt;&#xD;
&lt;p&gt;4. 不出意外的话，会在src目录生成几个dll和exe，全拷贝到其他目录里，比如c:/line&lt;/p&gt;&#xD;
&lt;p&gt;5. 从cygwin的安装目录里找到cygwin1.dll, cyggcc_s-1.dll这两个文件，拷贝到你的目录里&lt;/p&gt;&#xD;
&lt;p&gt;6. 找旧一点的linux程序和库（确保没有NPTL，这个我还没想到办法实现。。。），考到你的目录里，保留目录结构，确保根目录就是你的目录&lt;/p&gt;&#xD;
&lt;p&gt;7. 运行cmd，进入你的目录，执行&amp;rdquo;line.exe bash &amp;ldquo;命令&lt;/p&gt;&#xD;
&lt;p&gt;截图&lt;/p&gt;&#xD;
&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/gussing/201109/201109040224194482.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" alt="image" src="http://images.cnblogs.com/cnblogs_com/gussing/201109/201109040224204614.png" border="0" height="463" width="689" /&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;img src="data:image/png;base64,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" alt="" /&gt;&lt;/p&gt;&#xD;
&lt;p&gt;玩的愉快&lt;/p&gt;&lt;img src="http://www.cnblogs.com/gussing/aggbug/2165968.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/gussing/archive/2011/09/04/2165968.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/gussing/archive/2011/08/25/2153412.html</id><title type="text">LINE: 在windows上运行原生linux程序 (2) : demo完成</title><summary type="text">在大约一年前提到过，我想做这样一件事：打通windows上的int 80中断，让原生的linux程序也可以在windows上跑。中间因为公司项目紧，再加上idt的一个小问题困惑了我很久，所以搁置了一段时间。最近觉得周末实在闲的慌又把这项目捡起来了，并且在某次抽烟的时候突然想到“每个处理器都有自己的idt表”这一小常识，idt的问题也就很顺利的解决了。接下来的事情就变得很顺了，按照原定计划先把所有的系统调用转到用户态然后从cygwin1.dll里再过一遍，大约熬了两天夜，一个小小的demo就完成了。代码参考了很多的开源项目，比如cygwin, glibc, linux kernel, 还有一个死</summary><published>2011-08-25T07:43:00Z</published><updated>2011-08-25T07:43:00Z</updated><author><name>gussing</name><uri>http://www.cnblogs.com/gussing/</uri></author><link rel="alternate" href="http://www.cnblogs.com/gussing/archive/2011/08/25/2153412.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/gussing/archive/2011/08/25/2153412.html"/><content type="html">&lt;p&gt;在大约一年前&lt;a href="http://www.cnblogs.com/gussing/archive/2009/10/30/1593187.html"&gt;提到过&lt;/a&gt;，我想做这样一件事：打通windows上的int 80中断，让原生的linux程序也可以在windows上跑。中间因为公司项目紧，再加上idt的一个小问题困惑了我很久，所以搁置了一段时间。最近觉得周末实在闲的慌又把这项目捡起来了，并且在某次抽烟的时候突然想到&amp;ldquo;每个处理器都有自己的idt表&amp;rdquo;这一小常识，idt的问题也就很顺利的解决了。接下来的事情就变得很顺了，按照原定计划先把所有的系统调用转到用户态然后从cygwin1.dll里再过一遍，大约熬了两天夜，一个小小的demo就完成了。&lt;/p&gt;&#xD;
&lt;p&gt;代码参考了很多的开源项目，比如&lt;a href="http://cygwin.org/"&gt;cygwin&lt;/a&gt;, &lt;a href="http://www.gnu.org/"&gt;glibc&lt;/a&gt;, &lt;a href="http://kernel.org/"&gt;linux kernel&lt;/a&gt;, 还有一个死了很久的项目 &lt;a href="http://sourceforge.net/projects/line/"&gt;LINE&lt;/a&gt;。这事说起来也好玩，我最初想做这个项目的时候，压根没到sourceforge上去找过，包括实现原理，项目名称等都是自己想出来的。结果做了一半到sourceforge上一搜，居然有个一摸一样想法的东西早在2001年就存在了，连名字都想的一样。然后再到google上一搜，无数人都想到过类似的方案了，而且名字无一例外的打算叫成&amp;ldquo;LINE&amp;rdquo;。不得不说在开源的世界里想真正的创点新还真是困难。所幸sourceforge上的这个项目老的不成样了，是2001年的，看代码只能在win98上跑，而且是半成品，连编译都通不过，所以我做的这些事情也不是完全没价值。&lt;/p&gt;&#xD;
&lt;p&gt;不过看我这个项目仍然继承了sourceforge上这个LINE的很多东西，比如把系统调用反射回用户态这事，我原来的打算是插一个APC反射回去，看了下他们的做法，是直接把调用栈上保存的返回地址改成了用户态的一个SyscallHandler，有点类似缓冲区溢出注入的技术，这个方法毫无疑问的更合理更优雅。再比如，绝大多数的系统调用转回到用户态后只是简单的把参数包一包就转cygwin上去了，这部分代码属于纯体力劳动，我就很不客气的也照抄过来了。总之，之前我把这个项目定性为我的&amp;ldquo;创新&amp;rdquo;，现在我把它定性为&lt;a href="http://sourceforge.net/projects/line/"&gt;LINE&lt;/a&gt;项目的后续开发，虽然少了重新发明轮子的快感，不过坦白我也已经过了追求这种快感的年龄了。&lt;/p&gt;&#xD;
&lt;p&gt;在开发过程中碰到的主要问题有三个：idt的问题，某些较新的linux系统调用没法实现的问题，以及路径问题。我之前设置int 80中断的时候，都是在一个IOCTL里直接就调sidt指令，以为这么做就能改掉系统idt表，实际上由于每个处理器有自己的idt表，这么干只能改IOCTL发起者所在处理器上的idt表，到了运行调试的时候一个int 80中断的发起者到底是不是在该处理器上根本没法保证，所以有很长一段时间里我就处于这么一个状况：设置好idt后一调试，好的；再调试，异常了。搞的我很莫名其妙。后来我在IOCTL处理函数里往每个处理器都插一个DPC，然后在DPC里改idt，这种时好时坏的情况就再也没发生了。&lt;/p&gt;&#xD;
&lt;p&gt;等到用户态的响应函数写的差不多，简单的linux程序比如hello world等都能跑了后，我就开始从原生linux上抓真正的程序过来试。结果一跑问题就来了：linux 2.5.29 及以后的版本新增了几个系统调用&lt;a href="http://linux.die.net/man/2/set_thread_area"&gt;set_thread_area&lt;/a&gt;, &lt;a href="http://linux.die.net/man/2/get_thread_area"&gt;get_thread_area&lt;/a&gt; 等等，是为了对&lt;a href="http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library"&gt;NPTL&lt;/a&gt;做支持，这几个函数没办法在windows上模拟出来（至少我现在没想到办法），所以我就留空了，直接返回-ENOSYS （事实上190号以后的系统调用我全留空了）。但是现在的glibc还非用它不可，set_thread_area返回-ENOSYS后，glibc就直接打印error退出了。没办法，我只好在虚拟机上装了个redhat 6.0， 然后把那上面的程序和类库考出来用。RH 6.0 那可是史前时代的东西了，不会用到那么高级的东西，应该能跑。&lt;/p&gt;&#xD;
&lt;p&gt;最后还剩一个问题：windows的路径和*nix的路径表示完全不一致，比如linux上的程序想打开/lib/libc.so.6这个文件，在windows上根本就找不到。解决这个问题的方法倒是简单粗暴：我在open等文件操作的系统调用里做了一个小改动，凡是以&amp;lsquo;/&amp;rsquo;打头的路径名，我就把第一个&amp;rsquo;/&amp;rsquo;去掉让他变成相对路径，然后再用windows的open函数就能顺利的打开了。直到目前为止这个小技巧还是奏效的，从RH 6.0上拷过来的基本命令都能跑，有图为证：&lt;/p&gt;&#xD;
&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/gussing/201108/201108251543224354.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" alt="image" src="http://images.cnblogs.com/cnblogs_com/gussing/201108/201108251543256565.png" border="0" height="767" width="1105" /&gt;&lt;/a&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;当然，这东西现在还只是demo而已，问题还不少。比如跑bash还是有点小问题：bash能启动，但是在bash里打ls命令又是文件未找到。再比如只能在32位机器上跑，再比如很不稳定等。总之，后续还有很多工作要做。&lt;/p&gt;&#xD;
&lt;p&gt;源代码目前还没脸放出来，等整理完后会放到google code上，沿用GPL v2。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/gussing/aggbug/2153412.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/gussing/archive/2011/08/25/2153412.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/gussing/archive/2011/03/28/1997370.html</id><title type="text">wdk tips (7.2): IRP的CancelRoutine</title><summary type="text">上回我们留下一个未解的问题，就是当一个IRP的CancelRoutine没有被设置时，CancelIo操作会失败，系统中有可能会留下永远都不会被complete的IRP。在Threaded IRP和non-threaded IRP一节中我们有谈到irp分为线程相关和非线程相关两种。倘若一个永远不complete的irp是非线程相关的，情况会稍微好一点，顶多系统中泄露了一个资源。倘若该irp是线程相关的，那事情就大了。thread IRP由IoManager生成并保留在线程的IRP队列里，负责处理该IRP的驱动在收到下层驱动的Complete事件后不会主动收回IRP的资源而是继续complete</summary><published>2011-03-27T17:09:00Z</published><updated>2011-03-27T17:09:00Z</updated><author><name>gussing</name><uri>http://www.cnblogs.com/gussing/</uri></author><link rel="alternate" href="http://www.cnblogs.com/gussing/archive/2011/03/28/1997370.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/gussing/archive/2011/03/28/1997370.html"/><content type="html">&lt;p&gt;上回我们留下一个未解的问题，就是当一个IRP的CancelRoutine没有被设置时，CancelIo操作会失败，系统中有可能会留下永远都不会被complete的IRP。在Threaded IRP和non-threaded IRP一节中我们有谈到irp分为线程相关和非线程相关两种。倘若一个永远不complete的irp是非线程相关的，情况会稍微好一点，顶多系统中泄露了一个资源。倘若该irp是线程相关的，那事情就大了。thread IRP由IoManager生成并保留在线程的IRP队列里，负责处理该IRP的驱动在收到下层驱动的Complete事件后不会主动收回IRP的资源而是继续complete给IoManager，由IoManager负责回收，并从线程IRP列表中删除该IRP。一个线程在退出前会遍历等待IRP队列里所有的IRP，直到它们全部被complete为止。倘若其中有一个irp永远不complete，那么线程就永远不退出，无论是ExitThread也好还是_endthreadex也好还是什么邪恶的暴力擦除数据强退也好，全都不顶用。线程不退出，进程也不能销毁(题外话：进程资源的回收动作由最后一个线程退出后发起，所谓的杀进程，其实是用apc给所有线程发起退出操作）。更糟糕的是，操作系统的关机过程都会被堵住，除了关电源，没有其他办法恢复，这一点简直比BSOD还糟糕。我们知道由user mode发起的IO操作最后都会翻译成threaded irp，这就是为什么我在7.1大谈特谈user mode线程的原因：这个陷阱连user mode程序也会掉进去。Bad dog!&lt;br /&gt;要解决这一点方法很简单目标很明确，那就是防止&amp;ldquo;永远不complete的irp&amp;rdquo;这种东西出现。一般的做法是加个线程或者timer并设置超时时间，时间一到就cancel这个irp。如果irp由user mode程序发起，那么就调用CancelIo；如果irp由驱动发起，则是调用IoCancelIrp。所有这些动作要生效的大前提是你的irp有CancelRoutine的存在，否则一切都是白搭。所以这里我有个经验要跟大家分享：&lt;span style="color: #0000ff;"&gt;任何时候都给你的irp设置CancelRoutine，并在CancelRoutine里Complete你的IRP!&lt;/span&gt;为方便起见我们选non-threaded irp做个例子，所有的代码都在内核态，免得各位看官看示例代码还要做上下文切换。以下便是代码：&lt;br /&gt;&lt;br /&gt;Sending thread:&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;IoSetCancelRoutine(Irp, MyCancelRoutine);&#xD;
devext-&amp;gt;SentIrp = Irp;&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;Canceling thread:&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;if (devext-&amp;gt;AllocatedIrp != NULL) {&#xD;
   IoCancelIrp(devext-&amp;gt;SentIrp);&#xD;
}&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&#xD;
&lt;p&gt;cancel routine里的内容都是标准步骤，不赘述。看起来已经完美无缺了，可惜拿到测试组一跑就BSOD，系统抱怨说一个irp被free了两次，肯定是有地方被疏忽了，对，我们很好的处理的例外情况，却漏掉了常规情况：irp也是可以正常complete的！假如我们的CompleteRoutine是这样的：&lt;/p&gt;&#xD;
&lt;p&gt;Completion routine:&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;PIRP irp;&#xD;
irp = devext-&amp;gt;SentIrp;&#xD;
devext-&amp;gt;SentIrp = NULL;&#xD;
IoFreeIrp(irp);&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;br /&gt;&lt;br /&gt;它和CancelRoutine里用到了同一个irp，这是典型的多线程重入问题，需要加锁保护。修改后的代码如下：&lt;br /&gt;&lt;br /&gt;Sending thread:&lt;br /&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;KeAcquireSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);&#xD;
devext-&amp;gt;SentIrp = Irp;&#xD;
KeReleaseSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;br /&gt;&lt;br /&gt;Canceling thread:&lt;br /&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;KeAcquireSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);&#xD;
if (devext-&amp;gt;AllocatedIrp != NULL) {&#xD;
   IoCancelIrp(devext-&amp;gt;SentIrp);&#xD;
}&#xD;
KeReleaseSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;br /&gt;Completion routine:&lt;br /&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;PIRP irp;&#xD;
&#xD;
KeAcquireSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);&#xD;
irp = devext-&amp;gt;SentIrp;&#xD;
devext-&amp;gt;SentIrp = NULL;&#xD;
KeReleaseSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);&#xD;
&#xD;
IoFreeIrp(irp);&#xD;
&#xD;
return STATUS_MORE_PROCESSING_REQUIRED;&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;br /&gt;&lt;br /&gt;又是一个完美的程序，半分钟修掉一个BSOD，还有比这更爽的吗？结果一测试问题更大：系统挂起没有任何反应了。经验告诉我们这是一个死锁：Cancel thread获得spin lock后调用IoCancelIrp，IoCancelIrp最终进入CancelRoutine，而CancelRoutine则调用了IoCompleteIrp并进入Complete routine并试图再次获得spin lock，而该死的spin lock在同一条线程里也是会死锁的，这就是最终原因。&lt;br /&gt;问题出在rip的完成上。设置了Cancel routine和Complete routine后，有两个点可以做irp的完成和回收动作，而这两个点只能有一个被执行。借用网上某牛的代码描述我们可以看到有以下几种情况：&lt;br /&gt;&lt;br /&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;// No cancellation:&#xD;
//   Cancelable--&amp;gt;Completed&#xD;
//&#xD;
// Cancellation, IoCancelIrp returns before completion:&#xD;
//   Cancelable --&amp;gt; CancelStarted --&amp;gt; CancelCompleted --&amp;gt; Completed&#xD;
//&#xD;
// Canceled after completion:&#xD;
//   Cancelable--&amp;gt; Completed -&amp;gt; CancelStarted&#xD;
//&#xD;
// Cancellation, IRP completed during call to IoCancelIrp():&#xD;
//   Cancelable --&amp;gt; CancelStarted -&amp;gt; Completed --&amp;gt; CancelCompleted&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;br /&gt;&lt;br /&gt;这跟同步还是两回事，同步是指两个点不能同时摸这个irp，一个摸完换另一个则是可以的，而我们要达到的目标是只要irp被其中的任何一个摸过了，另一个就不能再去摸它。为了达到这个目的，我们需要增加额外的变量记录irp被摸了几次这个信息.改造后的cancel过程如下 &lt;br /&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;if (InterlockedExchange((PVOID)&amp;amp;touched, IRPLOCK_CANCEL_STARTED) == IRPLOCK_CANCELABLE) {&#xD;
&#xD;
          //&#xD;
          // You got it to the IRP before it was completed. You can cancel&#xD;
          // the IRP without fear of losing it, because the completion routine&#xD;
          // does not let go of the IRP until you allow it.&#xD;
          //&#xD;
          IoCancelIrp(irp);&#xD;
&#xD;
          //&#xD;
          // Release the completion routine. If it already got there,&#xD;
          // then you need to complete it yourself. Otherwise, you got&#xD;
          // through IoCancelIrp before the IRP completed entirely.&#xD;
          //&#xD;
          if (InterlockedExchange(&amp;amp;touched, IRPLOCK_CANCEL_COMPLETE) == IRPLOCK_COMPLETED) {&#xD;
            IoCompleteRequest(irp, IO_NO_INCREMENT);&#xD;
          }&#xD;
        }&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;br /&gt;而改造后的complete过程则如下&lt;br /&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;if (InterlockedExchange((PVOID)&amp;amp;touched, IRPLOCK_COMPLETED) == IRPLOCK_CANCEL_STARTED) {&#xD;
    //&#xD;
    // Main line code has got the control of the IRP. It will&#xD;
    // now take the responsibility of completing the IRP.&#xD;
    // Therefore...&#xD;
    IoFreeIrp(Irp);&#xD;
    return STATUS_MORE_PROCESSING_REQUIRED;&#xD;
  }&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;br /&gt;简单点说就是在中间加入能表示状态信息的变量touched表征现在所处的状态，cancelable, cancel started, cancel complete, completed四个状态相互协调保证complete rip不会被调用两次。如同在tip 5里提到过的，这也是口耳相传下来的范式，基本上有cancel rip的地方都得这么写。&lt;br /&gt;&lt;img src="http://www.cnblogs.com/gussing/aggbug/1997370.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/gussing/archive/2011/03/28/1997370.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/gussing/archive/2011/03/27/1996677.html</id><title type="text">wdk tips (7.1): 线程的创建和销毁</title><summary type="text">虽然内核开发人员从一开始就要考虑多线程的问题，但用户态开发人员曾经有过一段美好的生活：他们只需关心一条线程（多半是UI线程）并且不必在乎太多性能问题：即使你在主逻辑里嵌套了无数层循环都没关系，该死的摩尔定律替你搞定一切问题。进入多核时代后，用户态开发人员终于发现了他们忽略很久的，但及其重要的一个技术点：多线程。朋友，好生活已经结束了，欢迎你来到混乱的时代。我知道现在来写这篇东西似乎不合时宜，因为网上已经有无数文章讨论过多线程问题了，各个社区还开发了一个又一个的线程框架帮你解决烦人的琐事，不过我今天的主要目的是为了引出某个内核开发中的棘手问题（就是7.2要讲的，先按下不表），所以各位看官先放小弟</summary><published>2011-03-26T17:24:00Z</published><updated>2011-03-26T17:24:00Z</updated><author><name>gussing</name><uri>http://www.cnblogs.com/gussing/</uri></author><link rel="alternate" href="http://www.cnblogs.com/gussing/archive/2011/03/27/1996677.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/gussing/archive/2011/03/27/1996677.html"/><content type="html">&lt;p&gt;虽然内核开发人员从一开始就要考虑多线程的问题，但用户态开发人员曾经有过一段美好的生活：他们只需关心一条线程（多半是UI线程）并且不必在乎太多性能问题：即使你在主逻辑里嵌套了无数层循环都没关系，该死的摩尔定律替你搞定一切问题。进入多核时代后，用户态开发人员终于发现了他们忽略很久的，但及其重要的一个技术点：多线程。朋友，好生活已经结束了，欢迎你来到混乱的时代。&lt;/p&gt;&#xD;
&lt;p&gt;我知道现在来写这篇东西似乎不合时宜，因为网上已经有无数文章讨论过多线程问题了，各个社区还开发了一个又一个的线程框架帮你解决烦人的琐事，不过我今天的主要目的是为了引出某个内核开发中的棘手问题（就是7.2要讲的，先按下不表），所以各位看官先放小弟一马，让我把旧事拉出来说说完。&lt;/p&gt;&#xD;
&lt;p&gt;说到多线程，最烦人的其实是同步问题。关于这一点我很赞同&lt;a href="http://www.osronline.com/"&gt;osr&lt;/a&gt;邮件列表里的joe老师的观点：用户态程序不应该出现（自定义的）锁，任何时候你发现自己需要考虑用锁来同步了，就说明你的设计出了问题。同步这摊子事我有一堆话要说，但不是今天，今天我要说另一个比较容易被忽略的点：线程的创建和销毁。许多人不知道如何正确的创建和销毁线程，我看到过无数错误的写法，程序奇迹般的运行正常，但错的就是错的，现在不出问题，不代表以后不会。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;创建&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;线程创建的api是CreateThread，关于这个api只有一条原则需要注意：绝对不要去用它。让我们把时间回退到上实际70年代，那时c语言刚诞生不久，c run time library也才成型，多任务还是个高级玩意儿，如果当时就有咨询公司这种东西，他们甚至可以靠培训多任务相关技术发大财。很自然的，c运行库的作者没有考虑多线程的问题，他们假设整个c语言程序只有一条线程，没有切换，自然也没有重入，所以c运行库里有数不清的全局变量，errno就是最著名的一个。后来多任务出现了，进程和线程的概念也相继登场，这些全局变量就变得棘手了：它们会被重入。仔细推敲我们可以发现，这些全局变量其实不应该是整个地址空间可见的，而应该每个线程一份拷贝才对。实际上现在的c库就是这么干的，微软的msvcr把errno等东西放在TLS（线程本地存储块）中，创建线程的时候分配，销毁线程的时候回收。但是CreateThread作为系统api才不会管这些屁事呢，人家是系统级的，c运行库跟它没关系，问题就出在这里：你敢说你写的程序不用c运行库，所有的工作都用纯api完成？别扯了，还是听话别碰CreateThread为妙。ms vc中有替代函数_beginthreadex/_endthreadex，任何时候都必须用他们创建销毁线程。如果你用的是其他厂商的c库，就用他们提供的线程函数--不管那是什么东西，有多蠢，用就对了--别碰CreateThread。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;销毁&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;没有哪样东西比线程的销毁更恶心人了。如同上面所说，ExitThread函数绝对不能碰，除此之外还需要注意的是：唯一正确的退出方式就是让它跑完所有代码自然退出。但很多时候自然退出根本就是一个奢望，假如你的主线程需要等所有线程退出后才能做下一件事，那么加一个超时时间就是非常必要的，因为你不能让主线程等太久，况且有些线程（特别是IO相关的线程）退不退的出还是个问题。倘若超时事件真的发生，我们就不得不做一件烂事：强制线程退出。这种做法隐患多多，我能想到的大概有以下几个：&lt;/p&gt;&#xD;
&lt;p&gt;1. 资源泄漏。假如线程开始的时候申请了内存，打开了文件，或者其他任何形式的资源，并且在自然退出前释放资源，那么&lt;span style="text-decoration: line-through;"&gt;_endthreadex&lt;/span&gt;&lt;span style="color: #ff0000;"&gt;TerminateThread&lt;/span&gt;后这些资源就泄漏了，没有人会去回收他们。&lt;/p&gt;&#xD;
&lt;p&gt;2. 锁的状态。假如线程开始的时候获得mutex，自然退出前释放，那么&lt;span style="text-decoration: line-through;"&gt;_endthreadex&lt;/span&gt;&lt;span style="color: #ff0000;"&gt;TerminateThread&lt;/span&gt;后mutex就进入ABANDONED状态，其他WaitSingleObject的点会返回WAIT_ABANDONED值。仔细想想你的代码有没有处理这个返回值，多半是没有吧&amp;hellip;&lt;/p&gt;&#xD;
&lt;p&gt;3. IO相关的问题。如果你的GetOverlapResult调用将Wait参数置为TRUE，那么在IRP被完成之前它是不会返回的，强行退出线程会引起驱动的误会，驱动以为只要Complete了这个IRP，app就会做某类事情，实际上app没做。更糟的是如果GetOverlapResult调用将Wait参数置为FALSE并在随后的代码里进行有超时的等待，等不到就CancelIO，类似这样：&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;res = GetOverlapResult(&amp;hellip;, FALSE);&#xD;
&#xD;
if( !res )&#xD;
&#xD;
{&#xD;
&#xD;
　　if( WAIT_TIMEOUT == WaitForSingleObject(overlap.hEvent, 5000）)&#xD;
&#xD;
　　{&#xD;
&#xD;
　　　　CancelIo(m_hDriver);&#xD;
&#xD;
　　}&#xD;
&#xD;
}&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;那么强退线程后CancelIo就有可能没被执行到，IRP可能永远都不会被Complete。没有什么比系统中存在一个永远不会complete的IRP更糟糕的事了，你的进程将永远杀不掉，系统的ShutDown过程也会被挂起，恭喜你拔电源把。&lt;/p&gt;&#xD;
&lt;p&gt;4. appverifier会直接crash进程。开发过程中一直挂着appverifier跑是个好习惯，它会把各种隐患暴露给开发人员。比如强退线程这一条，裸奔的程序还能继续执行下去，最终用户不会知道发生了什么事，但挂着appverifier的程序就会爆炸，烧掉你的硬盘，并引发9级地震。好吧我开玩笑的，但你的manager一定不会容忍crash这一点。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;span style="color: #ff0000;"&gt;5. &lt;span id="comment_body_2055696" &gt;如果调用了需要SEH才能实现的功能： raise/signal&lt;br /&gt;这与TLS不同， 少了一个__try块是不能再后面补上的。&lt;br /&gt;这才是比较严重的问题。&lt;br /&gt;但windows下开发， 使用signal的代码确实不多。&lt;/span&gt;&lt;span id="comment_body_2055696" &gt;(&lt;/span&gt;感谢OwnWaterloo提供）&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;p&gt;那么到底有没有办法安全的强退线程呢，其实还是有几个的，我能想到的有以下几种：&lt;/p&gt;&#xD;
&lt;p&gt;1. 设置信号通知目标线程退出。比如定义一个BOOL exitThread值，目标线程的大循环就该写成这样&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;while( !exitThread)&#xD;
&#xD;
{&#xD;
&#xD;
　　&amp;hellip;&#xD;
&#xD;
}&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;主线程则是这样：&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;exitThread = TRUE;&#xD;
&#xD;
WaitForSingleObject(m_hThread, INFINITE);&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;这种做法绝大多数情况下有效，但是有race condition，exitThread会被重入。改成这样会更好一点&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;while ( WaitForSingleObject(hExitEvent,0) != WAIT_TIMEOUT )&#xD;
&#xD;
{&#xD;
&#xD;
　　&amp;hellip;&#xD;
&#xD;
}&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;主线程&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;SetEvent(hExitEvent)；&#xD;
&#xD;
WaitForSingleObject(hThread, INFINITE);&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;但还是有问题，目标线程的逻辑里如果有调用WaitForSingleObject(&amp;hellip;, INFINITE)无限等某事件，那么它还是退不出来。&lt;/p&gt;&#xD;
&lt;p&gt;2. 在主线程里这么干：&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;SuspendThread(hThread);&#xD;
&#xD;
GetThreadContext(hThread, &amp;amp;THREADCONTEXT);&#xD;
&#xD;
THREADCONTEXT.eip = my_exit;&#xD;
&#xD;
SetThreadContext(hThread, &amp;amp;THREADCONTEXT);&#xD;
&#xD;
ResumeThread(hThread);&#xD;
&#xD;
WaitForSingleObject(hThread, INFINITE);&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;my_exit里释放资源，退出线程。这种做法除了IO相关问题外全都有效，写起来也很有快感，强行修改线程的执行路径，跟在内核里hook system call似的，太酷了，特黑客的感嚼。我对此的建议是：绝对不要这么做。&lt;/p&gt;&#xD;
&lt;p&gt;3. 把目标线程里的WaitForSingleObject换成WaitForSingleObejectEx，把Alertable参数设成TRUE，在主线程里这么干：&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;QueueUserAPC( UserAPCProc, hThread, 0 );&#xD;
WaitForSingleObject( hThread, INFINITE ); &#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;UserAPCProc啥事也不用干，空函数就行。执行完QueueUserAPC函数后，目标线程的WaitForSingleObejectEx函数会立刻被唤醒，并返回WAIT_IO_COMPLETION状态。这是windows核心编程的作者jeffrey大牛道出的天机。这段代码写着也很有快感，居然用到APC耶，我是高手有木有！但是我对此的建议还是：绝对不要这么干。&lt;/p&gt;&#xD;
&lt;p&gt;4. 把目标线程里的WaitForSingleObject换成WaitForMultipleObjects，并在目标线程里这么写：&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;while ( WaitForSingleObject(hExitEvent,0) != WAIT_TIMEOUT )&#xD;
&#xD;
{&#xD;
&#xD;
　　&amp;hellip;&#xD;
&#xD;
　　HANDLE events[2];&#xD;
&#xD;
　　events[0] = hExitEvent;&#xD;
&#xD;
　　events[1] = hYourAnotherEventThatHaveToWait;&#xD;
&#xD;
　　if( WAIT_OBJECT_0 == WaitForMultipleObjects(2, events, FALSE, INFINITE) ) // you have to exit thread&#xD;
&#xD;
　　{&#xD;
&#xD;
　　　　&amp;hellip;&#xD;
&#xD;
　　}&#xD;
&#xD;
}&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;主线程则是这样：&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;SetEvent(hExitEvent)；&#xD;
&#xD;
WaitForSingleObject(hThread, INFINITE);&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;br /&gt;&#xD;
&lt;p&gt;这是比较标准的做法，包括IO操作相关的一系列问题都能得到解决。个人建议如果你想让目标线程主动退出，最好采用这个手段，并且确保所有非主要线程里没有WaitForSingleObject这个函数出现。如果想等，就用WaitForMultipleObjects。&lt;/p&gt;&#xD;
&lt;p&gt;到这儿为止该说的差不多说完了，唯一的隐患就在IO那里。正确的退出要求线程从wait函数返回后执行CancelIo操作取消掉你的IRP，这需要驱动配合在IRP里设置CancelRoutine。假如没有CancelRoutine，那么CancelIo操作是失败的，前面讲的那些恐怖故事还是会发生。关于这一点，我打算下次再说。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/gussing/aggbug/1996677.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/gussing/archive/2011/03/27/1996677.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/gussing/archive/2011/01/24/1943182.html</id><title type="text">wdk tips (6): 那些有用的debug命令</title><summary type="text">不要再假装自己写的程序没bug了，不可能的，debug工具你早晚得用上。最常见的debug工具非printf（windows上用OutputDebugString函数）莫属，简单方便易学易用，但局限性也是显而易见的，首先它对debugee的影响很大，某些race condition的bug你要多加几个log它就重现不出来了，然后你把log去了发布给客户，结果又成了必现的bug，这种烂事咱们都碰到过，你懂的。其次log能打印的东西有限，有时候你加log追某个变量的值，追到最后发现是其他变量有问题，这时候你又得加log重新跑。最后分析log的过程及其枯燥无聊，而在debug上敲命令分析则充满了乐趣</summary><published>2011-01-24T07:12:00Z</published><updated>2011-01-24T07:12:00Z</updated><author><name>gussing</name><uri>http://www.cnblogs.com/gussing/</uri></author><link rel="alternate" href="http://www.cnblogs.com/gussing/archive/2011/01/24/1943182.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/gussing/archive/2011/01/24/1943182.html"/><content type="html">&lt;p&gt;不要再假装自己写的程序没bug了，不可能的，debug工具你早晚得用上。最常见的debug工具非printf（windows上用OutputDebugString函数）莫属，简单方便易学易用，但局限性也是显而易见的，首先它对debugee的影响很大，某些race condition的bug你要多加几个log它就重现不出来了，然后你把log去了发布给客户，结果又成了必现的bug，这种烂事咱们都碰到过，你懂的。其次log能打印的东西有限，有时候你加log追某个变量的值，追到最后发现是其他变量有问题，这时候你又得加log重新跑。最后分析log的过程及其枯燥无聊，而在debug上敲命令分析则充满了乐趣。我知道有些人对debugger持有鄙视的态度，&amp;ldquo;单步调试是程序员的耻辱&amp;rdquo;云云。其实我想说的是，有好工具在手上干嘛不用，又不会怀孕，怕什么。&lt;/p&gt;&#xD;
&lt;p&gt;我们今天要来聊聊windbg，windows上debug的神器，（个人感觉）gdb也不如它。不是说windbg本身写的多么多么好，它牛逼的地方在于它是可扩展的，windows上的内核开发人员驱动开发人员用了windbg这么多年，该碰到的问题都碰到过了，该提的需求都提了，该写的扩展也都写了，导致现在的windbg强大无比，基本啥问题都能解决。就算你的需求很奇葩别人没想过，你也可以用windbg提供的开发框架自己写一个扩展，不费什么时间。所以我们看，提供扩展功能的工具都会变成神器，浏览器如此，debug工具也是一样。废话不多说，让我们来看几个非常有用的扩展：&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;!Drvobj&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;这个命令接收一个driver的名字作为参数，解析名字并找到对应的driver object，然后把由这个驱动程序产生的device object打印出来，如下：&lt;/p&gt;&#xD;
&lt;p&gt;lkd&amp;gt; !drvobj \Driver\usbhub   &lt;br /&gt;Driver object (8999a4f8) is for:    &lt;br /&gt; \Driver\usbhub    &lt;br /&gt;Driver Extension List: (id , addr) &lt;/p&gt;&#xD;
&lt;p&gt;Device Object list:   &lt;br /&gt;89701de8&amp;nbsp; 89a3f330&amp;nbsp; 8974ede8&amp;nbsp; 8977cc98    &lt;br /&gt;8985ac98&amp;nbsp; 89862c98&amp;nbsp; 898b4c98&amp;nbsp; 89add030    &lt;br /&gt;89876c98&amp;nbsp; 89849aa8&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;我们看到，usbhub驱动程序对应的driver object是8999a4f8，由它生成了10个device object。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;!Devobj&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;这个命令接收device object指针作为参数，并打印出该device object的内容，包括当前处理的irp，refrence count，device extension，以及与这个device object相关的上层驱动和下层驱动等，如下&lt;/p&gt;&#xD;
&lt;p&gt;lkd&amp;gt; !devobj 89701de8&amp;nbsp; &lt;br /&gt;Device object (89701de8) is for:    &lt;br /&gt; USBPDO-9 \Driver\usbhub DriverObject 8999a4f8    &lt;br /&gt;Current Irp 00000000 RefCount 0 Type 00000022 Flags 00003040    &lt;br /&gt;Dacl e16dcf84 DevExt 89701ea0 DevObjExt 89701fd0 DevNode 89998df0     &lt;br /&gt;ExtensionFlags (0000000000)&amp;nbsp; &lt;br /&gt;AttachedDevice (Upper) 89712b20 \Driver\HidUsb    &lt;br /&gt;Device queue is not busy.&lt;/p&gt;&#xD;
&lt;p&gt;&lt;br /&gt;lkd&amp;gt; !devobj 89a3f330&amp;nbsp; &lt;br /&gt;Device object (89a3f330) is for:    &lt;br /&gt; USBPDO-8 \Driver\usbhub DriverObject 8999a4f8    &lt;br /&gt;Current Irp 00000000 RefCount 0 Type 00000022 Flags 00003040    &lt;br /&gt;Dacl e16dcf84 DevExt 89a3f3e8 DevObjExt 89a3f518 DevNode 899b23c8     &lt;br /&gt;ExtensionFlags (0000000000)&amp;nbsp; &lt;br /&gt;AttachedDevice (Upper) 8971cb90 \Driver\TcUsb    &lt;br /&gt;Device queue is not busy.&lt;/p&gt;&#xD;
&lt;p&gt;我们查看了usbhub产生的10个device中的前两个，可以看出其中有一个是hidusb，另一个是tcusb。顺着AttachedDevice打印出的内容我们可以手动遍历整个驱动栈，不过这看起来有些麻烦，万幸有人以经写好一个扩展可以帮我们遍历了，那就是&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;!devstack&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;该命令也接收device object作为参数，并遍历着把该object以下的驱动栈全部打印出来，直到bus driver为止，如下&lt;/p&gt;&#xD;
&lt;p&gt;lkd&amp;gt; !devstack 89a3f330   &lt;br /&gt;&amp;nbsp; !DevObj&amp;nbsp;&amp;nbsp; !DrvObj&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; !DevExt&amp;nbsp;&amp;nbsp; ObjectName    &lt;br /&gt;&amp;nbsp; 8971cb90&amp;nbsp; \Driver\TcUsb&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 8971cc48&amp;nbsp; 000000ae    &lt;br /&gt;&amp;gt; 89a3f330&amp;nbsp; \Driver\usbhub&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 89a3f3e8&amp;nbsp; USBPDO-8    &lt;br /&gt;!DevNode 899b23c8 :    &lt;br /&gt;&amp;nbsp; DeviceInst is "USB\Vid_0483&amp;amp;Pid_2016\5&amp;amp;39a18bdd&amp;amp;0&amp;amp;2"    &lt;br /&gt;&amp;nbsp; ServiceName is "TcUsb"&lt;/p&gt;&#xD;
&lt;p&gt;可以看到tc usb在usbhub之上，而usbhub则是硬件"USB\Vid_0483&amp;amp;Pid_2016\5&amp;amp;39a18bdd&amp;amp;0&amp;amp;2&amp;ldquo;的bus driver。前面有篇博文我们说到过windows里把几乎所有的资源都抽象成了一个"object&amp;rdquo;的概念，所有的object都有一个结构一致的object head，以方便提供统一的操作接口，以下命令就是打印出obect信息的命令：&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;！object&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;kd&amp;gt; !object \   &lt;br /&gt;Object: e1001300&amp;nbsp; Type: (8a65e2c0) Directory    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ObjectHeader: e10012e8 (old version)    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; HandleCount: 0&amp;nbsp; PointerCount: 39    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Directory Object: 00000000&amp;nbsp; Name: \    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 292 symbolic links snapped through this directory &lt;/p&gt;&#xD;
&lt;p&gt;Hash Address&amp;nbsp; Type&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Name   &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ---- -------&amp;nbsp; ----&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ----    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 00&amp;nbsp; e100b6e0 Directory&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ArcName    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 8a515030 Device&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Ntfs    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 01&amp;nbsp; e2726b88 Port&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SeLsaCommandPort    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 03&amp;nbsp; e1011488 Key&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; \REGISTRY    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 05&amp;nbsp; e2728888 Port&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ThemeApiPort    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 06&amp;nbsp; e16c7f68 Port&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; XactSrvLpcPort    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 09&amp;nbsp; e1d4d428 Directory&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; NLS    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 10&amp;nbsp; e1001078 SymbolicLink&amp;nbsp; DosDevices    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 13&amp;nbsp; e1c9e160 Port&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SeRmCommandPort    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 14&amp;nbsp; 8a577030 Device&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dfs&amp;hellip;&lt;/p&gt;&#xD;
&lt;p&gt;我们查看了根目录，并列出了它的所有子项（太多了，没全贴上来），它的功能跟winobj很像，不过没有winobj直观。再来看几个跟power有关的命令。我们知道用wdm写驱动最麻烦的事情之一就是所有的power命令都要自己handle，而wdf则帮我们全包圆了（又回到上会的讨论了不是），没包圆也有它的好处，就是你得强迫自己去理解这部分内容。power irp处理不好，机器很容易就不能进s3/s4或者不能从s3/s4唤醒，这时候我们就得借助windbg来追查问题到底出在哪儿，查看当前power状态的命令是&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;！podev&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;该命令接收device object为参数，打印它当前的power状态&lt;/p&gt;&#xD;
&lt;p&gt;lkd&amp;gt; !podev 89784b10&amp;nbsp; &lt;br /&gt;Device object is for:    &lt;br /&gt;&amp;nbsp; DriverObject 899d4410    &lt;br /&gt;Current Irp 00000000 RefCount 0 Type 00000002 DevFlags 00000050    &lt;br /&gt;Device queue is not busy.    &lt;br /&gt;Device Object Extension: 89784bc8:    &lt;br /&gt;PowerFlags: 00000000 =&amp;gt;SystemState=0 DeviceState=0    &lt;br /&gt;Dope: 00000000:&lt;/p&gt;&#xD;
&lt;p&gt;我们看到目前device处于d0(working)状态，系统处于s0(idle)状态。但是这个命令只能给我们一个总结，到底哪些power irp正在处理我们没法看出来。以下命令正是列出系统中所有power irp的&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;！poreqlist&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;lkd&amp;gt; !poreqlist   &lt;br /&gt;All active Power Irps from PoRequestPowerIrp    &lt;br /&gt;PopReqestedPowerIrpList    &lt;br /&gt;FieldOffset = 00000004    &lt;br /&gt;Irp 8a60ba20 DevObj 8a5c1d70 \Driver\ACPI Ctx 00000004&amp;nbsp;&amp;nbsp; Wait Wake S3    &lt;br /&gt;Irp 882f1e00 DevObj 89a05440 \Driver\usbuhci Ctx 00000001&amp;nbsp;&amp;nbsp; Wait Wake S0    &lt;br /&gt;Irp 883dee00 DevObj 89a2b218 \Driver\usbuhci Ctx 00000001&amp;nbsp;&amp;nbsp; Wait Wake S0    &lt;br /&gt;Irp 8843c008 DevObj 89a20528 \Driver\usbuhci Ctx 00000001&amp;nbsp;&amp;nbsp; Wait Wake S0    &lt;br /&gt;Irp 884ca220 DevObj 89a10030 \Driver\usbehci Ctx 00000001&amp;nbsp;&amp;nbsp; Wait Wake S0    &lt;br /&gt;Irp 883662d0 DevObj 89b36030 \Driver\usbehci Ctx 00000001&amp;nbsp;&amp;nbsp; Wait Wake S0    &lt;br /&gt;Irp 87ec2008 DevObj 8974ede8 \Driver\usbhub Ctx 00000001&amp;nbsp;&amp;nbsp; Wait Wake S0    &lt;br /&gt;Irp 89b4ec00 DevObj 899b7618 \Driver\usbuhci Ctx 00000001&amp;nbsp;&amp;nbsp; Wait Wake S0    &lt;br /&gt;Irp 882ca7d0 DevObj 89a3f330 \Driver\usbhub Ctx 00000001&amp;nbsp;&amp;nbsp; Wait Wake S0&lt;/p&gt;&#xD;
&lt;p&gt;我们看到很多的hid设备处于等待唤醒的状态。&lt;/p&gt;&#xD;
&lt;p&gt;最后我们来聊聊内核调试时如何调试用户态的东西。我们知道在进程的用户态部分相互隔离，而内核部分都是share同一个地址空间，由这特性带来的好处坏处我们先不谈，今天先关注具体问题。断在kernel里的debugger要调试别的进程的kernel部分很容易，因为地址是同一块，但是要调试user mode部分就不那么容易了。我们知道user mode是不可能直接访问内核地址的，cpu在将虚地址翻译成物理地址的时候会检查特权级，user mode是第3级而内核是第0级，倘若第三级的指令带的地址是第0级，cpu会抛拒绝访问的异常。反过来，内核指令访问user mode地址虽然可行，不过得考虑进程上下文，如果你不管进程上下文直接访问user mode地址，有两种错误情况会发生：你访问的根本不是你想要的进程，或者你访问的地址根本没有东西。地址空间分为用户态和内核态这种说法是站在用户态角度讲的，它假设所有的线程都有"用户态"部分，实际上PsCreateSystemThread产生的内核线程是没有用户态部分的，它附在一个叫"System&amp;rdquo;的进程上，而"System&amp;rdquo;进程只是为管理方便而存在的虚拟的东西，没有实体。所以我们说从内核debugger调试用户态内容远没有kernel调kernel，user调user那么简单，你必须关心进程上下文这个东西。下面这个命令可以列出系统中所有的进程:&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;！process&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;lkd&amp;gt; !process&amp;nbsp; -1 0   &lt;br /&gt;PROCESS 8854b020&amp;nbsp; SessionId: 0&amp;nbsp; Cid: 1060&amp;nbsp;&amp;nbsp;&amp;nbsp; Peb: 7ffd4000&amp;nbsp; ParentCid: 04b4    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; DirBase: 0abc0ae0&amp;nbsp; ObjectTable: e12e5650&amp;nbsp; HandleCount: 396.    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Image: windbg.exe&lt;/p&gt;&#xD;
&lt;p&gt;lkd&amp;gt; !process&amp;nbsp; 0 0   &lt;br /&gt;**** NT ACTIVE PROCESS DUMP ****    &lt;br /&gt;PROCESS 881de6f8&amp;nbsp; SessionId: 0&amp;nbsp; Cid: 1498&amp;nbsp;&amp;nbsp;&amp;nbsp; Peb: 7ffde000&amp;nbsp; ParentCid: 04b4    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; DirBase: 0abc0c20&amp;nbsp; ObjectTable: e465aa70&amp;nbsp; HandleCount:&amp;nbsp; 46.    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Image: notepad.exe&lt;/p&gt;&#xD;
&lt;p&gt;如上所示，当前进程是windbg，process object为8854b020，而我们关心notepad进程的process object是881de6f8 ,并不是同一个，我们首先要做的事情就是切换到notepad这个进程上去，使用内置命令.process&lt;/p&gt;&#xD;
&lt;p&gt;lkd&amp;gt; .process 881de6f8   &lt;br /&gt;Implicit process is now 881de6f8&lt;/p&gt;&#xD;
&lt;p&gt;然后f5让系统跑一会儿（windbg断住的时候，整个系统是挂起的，进程切换也不会发生），再次断下来的时候你就已经在notepad进程里了（上面列的打印都是在local kernel debug里的，功能很受限，比如系统没挂起，f5也不能用。接下来我要切换到双机调试模式）&lt;/p&gt;&#xD;
&lt;p&gt;kd&amp;gt; !process -1 0   &lt;br /&gt;PROCESS 863c22f0 SessionId: 0 Cid: 00bc Peb: 7ffdb000 ParentCid: 05fc    &lt;br /&gt;DirBase: 06c602c0 ObjectTable: e16718e8 HandleCount: 29.    &lt;br /&gt;Image: notepad.exe&lt;/p&gt;&#xD;
&lt;p&gt;虽然已经在notepad进程里了，但user mode的东西依然不可见，因为windbg会缓存用户态的信息，进程切换后，你得手工刷新缓存，用内置命令.reload /user。做完这一步后，user mode的信息就变得可见了，你可以在用户态函数里下断点：kd&amp;gt; bp /p @$proc ntdll!ntcreatefile，或者列出加载模块：lm 等等，就跟普通的用户态程序调试一摸一样。上面那一套步骤很烦，却是步步都不能省，有没有方法简化它呢？如开头所说，windbg是可扩展的，你想到的需求，别人早就想到，并且已经写好工具等你用了，以下命令做完一整套动作：&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;！bpid&lt;/p&gt;&#xD;
&lt;p&gt;该命令接收process cid作为参数，找到对应的进程并切换进程上下文，跑一会儿，断下来，刷新用户态内容，全部搞定。怎么样，是不是很方便？&lt;/p&gt;&lt;img src="http://www.cnblogs.com/gussing/aggbug/1943182.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/gussing/archive/2011/01/24/1943182.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/gussing/archive/2011/01/23/1942294.html</id><title type="text">wdk tips (5): 再谈IO_STACK_LOCATION：IoMarkIrpPending</title><summary type="text">IO_STACK_LOCATION很重要，再多聊一点也无妨。上上回我们谈了IO_STACK_LOCATION和那几个重要的函数，当然，我的目的不是扫盲，而是记下一些容易犯错的地方（实际上都是工作中碰到过的钉子）以方便自己回顾。我的记性是如此的差以至于几月不看就会忘记。如果你对这东西没概念，我建议你先多查查WDK文档。上回我们聊了IoCopyCurrentIrpStackLocationToNext和IoSkipCurrentIrpStackLocation的差别（你看我的记性是不是很差，其实是上上回说的），结果把要聊的核心内容给忘了。IO_STACK_LOCATION这坨东西出现的原因很大程度</summary><published>2011-01-22T17:38:00Z</published><updated>2011-01-22T17:38:00Z</updated><author><name>gussing</name><uri>http://www.cnblogs.com/gussing/</uri></author><link rel="alternate" href="http://www.cnblogs.com/gussing/archive/2011/01/23/1942294.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/gussing/archive/2011/01/23/1942294.html"/><content type="html">&lt;p&gt;IO_STACK_LOCATION很重要，再多聊一点也无妨。上上回我们谈了IO_STACK_LOCATION和那几个重要的函数，当然，我的目的不是扫盲，而是记下一些容易犯错的地方（实际上都是工作中碰到过的钉子）以方便自己回顾。我的记性是如此的差以至于几月不看就会忘记。如果你对这东西没概念，我建议你先多查查WDK文档。&lt;/p&gt;&#xD;
&lt;p&gt;上回我们聊了IoCopyCurrentIrpStackLocationToNext和IoSkipCurrentIrpStackLocation的差别（你看我的记性是不是很差，其实是上上回说的），结果把要聊的核心内容给忘了。IO_STACK_LOCATION这坨东西出现的原因很大程度上就是为了解决异步IO中出现的问题，所谓的异步IO，指的是受到irp请求后，并不在同一个线程上下文里就把所有的事情做完，而是先把irp放在队列里，然后返回pending，在其他线程里做完真正的事情后在complete这个irp。这里涉及到一个重要的函数IoMarkIrpPending，它会在irp中置一个&lt;span style="font-family: 'YaHei Consolas Hybird'; line-height: 19px; border-collapse: collapse;"&gt;SL_PENDING_RETURNED的flag告诉IoManager事情没做完，别急着回收资源。IoManager在调用irp分发函数返回时，会检查分发函数的返回值，如果返回的不是STATUS_PENDING而&lt;/span&gt;&lt;span style="font-family: 'YaHei Consolas Hybird'; line-height: 19px; border-collapse: collapse;"&gt;SL_PENDING_RETURNED flag已经被置上，那么BSOD将会发生。我不知道你是怎么想的，反正我看到两个域，两个线程，一致这些词同时出现就觉得麻烦又要来了，事实证明这档子还真有那么点麻烦。&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;span style="font-family: 'YaHei Consolas Hybird';" face="'YaHei Consolas Hybird'"&gt;&lt;span style="border-collapse: collapse; line-height: 19px;"&gt;让我们研究点具体的问题。&amp;ldquo;&lt;/span&gt;&lt;/span&gt;先把irp放在队列里，然后返回pending，在其他线程里做完真正的事情&amp;rdquo;这句话翻译成代码大概会是这么个样子&amp;lt;1&amp;gt;&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;DriverDispatch:&#xD;
   IoMarkIrpPending      (1)&#xD;
   InsertToList&#xD;
   return STATUS_PENDING&#xD;
&#xD;
work item routine:&#xD;
   IoSkipCurrentIrpStackLocation                (2)&#xD;
   IoCallDriver&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;这些代码看上去没错，实际隐含了一个问题：IoCallDriver将irp分发给下层驱动后，而下层驱动的行为你是没法控制的。我们假设下层驱动的处理函数是这么写的&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;Irp-&amp;gt;IoStatus.Status = STATUS_SUCCESS;&#xD;
    IoCompleteRequest(Irp, 0);&#xD;
    return STATUS_SUCCESS;        (3)&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;代码（1）中将&lt;span style="font-family: 'YaHei Consolas Hybird'; line-height: 19px; border-collapse: collapse;"&gt;SL_PENDING_RETURNED flag置上，代码（2）调用的是skip，也就是说下层驱动还是用同一层location，&lt;/span&gt;&lt;span style="font-family: 'YaHei Consolas Hybird'; line-height: 19px; border-collapse: collapse;"&gt;SL_PENDING_RETURNED也还存在，代码（3）却返回了一个STATUS_SUCCESS，bang！BSOD发生了。你的驱动是没问题，但你把人家的弄蓝屏了这怎么行。也许你要抱怨下层驱动为何变成同步了，但是我也老实跟你讲，别对人家的代码做任何假设，特别是人家的逻辑完全没错的情况下。那怎么办呢，我首先想到的是把skip函数换成copy函数，如下&amp;lt;2&amp;gt;&lt;/span&gt;&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;DriverDispatch:&#xD;
   IoMarkIrpPending      &#xD;
   InsertToList&#xD;
   return STATUS_PENDING&#xD;
&#xD;
Thread:&#xD;
   IoCopyCurrentIrpStackLocationToNext                         (4)     &#xD;
   IoSetCompleteRoutine&#xD;
   IoCallDriver&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;这么写我还是担心下层驱动会BSOD，因为代码(4)把本层的所有信息都拷贝到下层去了，SL_PENDING_RETURNED依然存在嘛。或许改成如下形式可行&amp;lt;3&amp;gt;&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;DriverDispatch:&#xD;
       IoCopyCurrentIrpStackLocationToNext&#xD;
       IoSetCompleteRoutine&#xD;
       IoMarkIrpPending&#xD;
       InsertIntoList&#xD;
       return STATUS_PENDING;&#xD;
&#xD;
   Thread:&#xD;
       IoCallDriver&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;这段代码能work，但是很丑陋，IoCopyCurrentIrpStackLocationToNext的逻辑明显是跟Thread要做的事情有关系的，事实上这个函数一般是紧跟着IoCallDriver一起使用的。这下我就没什么办法了，怎么做都不对，只好去新闻组里问问题。结果确实出乎意料的，这个问题早就有人问过了，并且答案也很简单，&amp;lt;2&amp;gt;其实是对的，我对IoCopyCurrentIrpStackLocationToNext函数的理解有误，我以为它只是单纯的拷贝内容，实际上这个函数除了拷贝还做了另外一些事情，它的实现如下&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;PIO_STACK_LOCATION irpSp;&#xD;
PIO_STACK_LOCATION nextIrpSp;&#xD;
irpSp = IoGetCurrentIrpStackLocation(Irp);&#xD;
nextIrpSp = IoGetNextIrpStackLocation(Irp);&#xD;
RtlCopyMemory( nextIrpSp, irpSp, FIELD_OFFSET(IO_STACK_LOCATION, CompletionRoutine));&#xD;
nextIrpSp-&amp;gt;Control = 0;      （5）&#xD;
&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;仔细看代码（5），它把Control域里的flag清空了。到此为止IoMarkIrpPending引起的问题事情圆满得到解决。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;聊到这里我要发发感慨：要自己山寨一个调用栈，还想让它工作在多线程环境下是多么的不容易。在这里我不得不说点难听的：这几个函数其实没有好好design过，看那写个长的吓人的名字，还有那么多side effect，正确性全靠口耳相传的规则保证，山寨这两字用在这上面再贴切不过。引用新闻组里某牛的说法，当我看到IoSkipCurrentIrpStackLocation这个函数时，我猜想它的行为应该是告诉IoManager本层location的东西我完全没动，下层驱动也千万别来改我的东西，谁会想到下层驱动是可以改本层内容的。倘若我们自己来设计一个分层的模块，各层之间数据相互独立应该是个常识，诸位写MVC的恐怕不会让Ｃ保留一个Ｖ的指针随便修改吧。并且&amp;lt;2&amp;gt;这种代码片段应该是异步IO的范式，其他写法几乎都是错的，那为何不直接抽象出一个函数做这些事情呢，靠口耳相传算怎么回事。&lt;/p&gt;&#xD;
&lt;p&gt;说到这里我又有新的感慨，坦率讲啥事都封装好让程序员来调用的做法我是不欣赏的，因为这会阻碍程序员的进步。如果这种函数真的存在，我这篇博文恐怕就没机会写了，我甚至都可能不知道有这么回事。看着.net,java等框架那么多设计良好封装严实的接口，有谁知道里面都藏了些什么故事，又有几个人会去关心一个行为正确的api后面有多少的汗水和无奈。想反，封装不好的函数则会强迫你去理解问题的本质，这对程序员的自我提升是很有好处的。我承认如果想把IT这个行当工业化，封装等事是必须做的，程序员的水平好坏差别巨大，完全没办法用统一尺度估量生产力，生产力不能估量那么进度，质量等的估量也无从谈起，而封装起到的作用是把所有的程序员尽可能拉倒同一水平级，上下偏差尽量小，让估量的工作有那么点实施的可能。站在老板的角度我当然希望程序员是完全无差别的，就跟流水线上的机器人手臂一样最好。但我不是老板，我是程序员，我可不希望当什么劳什子的机器人手臂，从某种角度将，我倒是希望完全没现成api最好，逼着自己什么都得知道。当然，这种想法有点极端，工业化还是要做的，否则IT项目那么高的失败率就没法将下来。幸运的是折中的方法也还是存在的，那就是借助开源：工业化接着搞，api接着封装，机器人手臂接着当，但有时间有精力多看看开源代码，看看那堆api后面到底都有些什么玩意儿，这对我们自己有好处。设计良好的类库是老板的好朋友，而开源是程序员的好朋友，没事别到xxBeta或者啥啥地方去当喷子，现在这世道替老板说话的人已经太多了，而替程序员说话的却极少，偶尔有时候也该替自己多想想。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/gussing/aggbug/1942294.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/gussing/archive/2011/01/23/1942294.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/gussing/archive/2011/01/21/1941128.html</id><title type="text">wdk tips (4): 当你CreateFile时，你都干了些什么</title><summary type="text">今天我们聊一聊CreateFile，这个名字取的不合适但IO的世界里完全绕不过去的东西，以及与之相关的“namespace”这一概念。我们知道Create的意思是创造，创建，上帝创造了这个世界，指的可不是上帝打开了某样存在的东西（唯物主义者，我知道你们有意见，给我闭嘴…），但这个倒霉的函数要做的却是打开。我们也知道File是文件，windows里面也没有“一切都是文件”的概念，但这个倒霉的函数要做的却是打开所有能返回handle的内核对象。Anyway，CreateFile函数是唯一一个能打开内核对象的handle，并让user mode app来访问的方法。将范围缩小到驱动，这个函数也是唯一</summary><published>2011-01-21T06:34:00Z</published><updated>2011-01-21T06:34:00Z</updated><author><name>gussing</name><uri>http://www.cnblogs.com/gussing/</uri></author><link rel="alternate" href="http://www.cnblogs.com/gussing/archive/2011/01/21/1941128.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/gussing/archive/2011/01/21/1941128.html"/><content type="html">&lt;p&gt;今天我们聊一聊CreateFile，这个名字取的不合适但IO的世界里完全绕不过去的东西，以及与之相关的&amp;ldquo;namespace&amp;rdquo;这一概念。&lt;/p&gt;&#xD;
&lt;p&gt;我们知道Create的意思是创造，创建，上帝创造了这个世界，指的可不是上帝打开了某样存在的东西（唯物主义者，我知道你们有意见，给我闭嘴&amp;hellip;），但这个倒霉的函数要做的却是打开。我们也知道File是文件，windows里面也没有&amp;ldquo;一切都是文件&amp;rdquo;的概念，但这个倒霉的函数要做的却是打开所有能返回handle的内核对象。Anyway，CreateFile函数是唯一一个能打开内核对象的handle，并让user mode app来访问的方法。将范围缩小到驱动，这个函数也是唯一一个能打开驱动的handle以便user mode app访问驱动的方法。CreateFile带的参数中有一个lpFileName指定了内核对象的名字，如果该名字指代的是一个驱动，那么拥有该名字的PDEVICE_OBJECT的IRP_MJ_CREATE例程就会被调用到，返回的handle则是该object的代理（详情请参看本博中跟handle有关的那几篇），在这个handle上调用read，write，DeviceIoControl等操作最后都会落到PDEVICE_OBJECT相应的IRP_MJ_XX上。IoManager在打开handle的过程中扮演了至关重要的角色，没有它的帮忙，user mode和kernel mode的通信将会非常困难，除非你觉得每写一个驱动就增加一个系统调用很简单很有趣。那么IoManager都做了些什么了，一般是以下几步：&lt;/p&gt;&#xD;
&lt;ol&gt;&#xD;
&lt;li&gt;解析lpFileName参数。文件名里的内容对user mode有意义，但对内核不一定有意义。一般情况下这个名字都是一个symbol link，简单点可以把它想成是一个别名，link的target是真正的名字，就好比地海中的魔法师都有真名，只有知道了真名，你才能打败他。举个例子，"C:\&amp;rdquo;这个盘符就是一个symbol link，它的真名是"\Device\HarddiskVolume1&amp;rdquo;&lt;/li&gt;&#xD;
&lt;li&gt;如果名字存在，那么找到相应的内核对象。讨论范围缩小到驱动就是一个PDEVICE_OBJECT。&lt;/li&gt;&#xD;
&lt;li&gt;如果解析完名字后还有字符串剩下，则把这剩下的字符串贴在FileName域里&lt;/li&gt;&#xD;
&lt;li&gt;生成一个IRP，交给驱动处理。&lt;/li&gt;&#xD;
&lt;/ol&gt;&#xD;
&lt;p&gt;注意，这里有个安全漏洞：如果FileName域为NULL，那么打开handle的时候会做权限检查。如果不为NULL，则默认不检查，即使你的驱动里放了空空的动作片，你父母想访问就能访问。你肯定不想让这种事发生。所以在创建PDEVICE_OBJECT时记得把FILE_DEVICE_SECURE_OPEN标志位加到Characteristics域中去，这会强制IoManager检查权限，即使FileName不为NULL。&lt;/p&gt;&#xD;
&lt;p&gt;让我们举几个例子，假设你要对"C:\av\rio.avi&amp;rdquo;做CreateFile，IoManager首先会解析这个字符串，它发现"C:\&amp;rdquo;指的是"\Device\HarddiskVolume1&amp;rdquo;，并且"\Device\HarddiskVolume1&amp;rdquo;在namespace中存在，那么它将寻找"\Device\HarddiskVolume1&amp;rdquo;对应的object，找到后创建IRP，将"\av\rio.avi&amp;rdquo;贴在FileName域里，并将irp转给相应的object处理。因为FileName里的内容不为空，所以IoManager不会检查权限，你就祈求上帝FILE_DEVICE_SECURE_OPEN标志位已经被设置了吧。但是如果你对"c:\&amp;rdquo;做访问，那么FileName域为NULL，不管标志位有没有设置，权限检查的动作都会做。&lt;/p&gt;&#xD;
&lt;p&gt;关于symbol link和真实姓名的信息全部在一个叫name space的东西里，这东西和文件系统一样，也用树形结构管理，根部是名字"\&amp;rdquo;。用牛逼工具WinObj可以清楚的看到整个name space的信息，如截图所示&lt;/p&gt;&#xD;
&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/gussing/201101/201101211433594573.png"&gt;&lt;img border="0" width="708" src="http://images.cnblogs.com/cnblogs_com/gussing/201101/201101211433593800.png" alt="image" height="533" style="display: inline; border: 0px;" title="image" /&gt;&lt;/a&gt; &lt;/p&gt;&#xD;
&lt;p&gt;个人认为这才是真正的文件系统，与*NIX中的文件系统相对应，而C盘D盘等所谓的盘符其实是文件系统中的某个目录，被Explorer这个Window Manager放在首页了而已。盘符是*NIX程序员最喜欢的吐槽点，没有之一，但其实nt内核里的目录（某种程度上说）也是单根的，所谓盘符就是个别名，mount等操作其实也很容易实现。linux中内核可以注册虚拟目录，比如/proc，/dev这种，nt里也可以在name space里添加自己想要的东西，将这个名字与自己的驱动绑定，能做的事情跟虚拟目录实在很像。说到这里我又想感慨一下，操作系统这么手艺，发展到今天大家在某些关键点上的选择那是出奇的一致。反而是那些user mode的东西则是各有个的特色，大家会争的面红耳赤。那些个满嘴"UNIX&amp;rdquo;哲学的哲学家，我可以肯定他没接触过内核。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/gussing/aggbug/1941128.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/gussing/archive/2011/01/21/1941128.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/gussing/archive/2011/01/20/1940196.html</id><title type="text">wdk tips (3): IRQL</title><summary type="text">今天我们来聊聊IRQL，这是驱动新手的梦魇，想想看多少BSOD是因为IRQL不对引起的。这也是*NIX类内核开发人员最喜欢的吐槽点之一，你看linux里就没有这个概念，我们还不是活的好好的？我偶尔有时候能得着一些空，也会问一样的问题：为毛？为毛要有这东西存在！后来我想通了。我们先聊passive level和interrupt level。passive level是普通级别，同时也是优先级最低的，所有的用户态线程和大部分的内核态线程都会在这个级别上运行。interrupt level则是中断服务例程的运行级别。这两者有差很好理解，几乎所有os教程里都有告诫我们中断服务例程要尽可能快的完成，并</summary><published>2011-01-20T06:39:00Z</published><updated>2011-01-20T06:39:00Z</updated><author><name>gussing</name><uri>http://www.cnblogs.com/gussing/</uri></author><link rel="alternate" href="http://www.cnblogs.com/gussing/archive/2011/01/20/1940196.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/gussing/archive/2011/01/20/1940196.html"/><content type="html">&lt;p&gt;今天我们来聊聊IRQL，这是驱动新手的梦魇，想想看多少BSOD是因为IRQL不对引起的。这也是*NIX类内核开发人员最喜欢的吐槽点之一，你看linux里就没有这个概念，我们还不是活的好好的？我偶尔有时候能得着一些空，也会问一样的问题：为毛？为毛要有这东西存在！后来我想通了。&lt;/p&gt;&#xD;
&lt;p&gt;我们先聊passive level和interrupt level。passive level是普通级别，同时也是优先级最低的，所有的用户态线程和大部分的内核态线程都会在这个级别上运行。interrupt level则是中断服务例程的运行级别。这两者有差很好理解，几乎所有os教程里都有告诫我们中断服务例程要尽可能快的完成，并且不能被其他线程抢占，也不能主动交出运行权（也就是sleep）。nt内核里的中断没有属于自己的栈，它会抢占当前运行线程的栈使用，抢到谁就算谁倒霉，linux内核似乎看配置，栈大小配为4k的时候也是抢别人的用。倘若中断服务例程可以被抢占（也就是换出），那么这些上下文就存在了别人的栈顶上。考虑这么一种情况：中断服务例程抢了线程T1的栈使用，运行到一半被线程T2抢占了，T2这个倒霉催的刚运行一点点时间也被换出，新上位的正好是T1，这下问题就来了。T1并不是因为线程切换才被抢占的，它是被中断抢了，中断换出前T1的上下文并没有被保留，所以恢复也成了不可能完成的任务。为了防止此类情况的发生，中断服务例程必须不能被其他线程抢占，它在T1的栈上做事情，做完后清理栈然后把还给T1继续运行，在T1看来就跟什么都没发生过一样。要做到这一点其实很容易，只要在运行中断服务例程前关闭所有其他中断即可，事实上linux也真是这么做的（如果我错了请千万告诉我），但是nt内核的设计者想的就有点多，他们觉得关闭所有中断似乎太暴力，即使名字都叫中断，那也得有个轻重缓急之分嘛，断电的中断就比硬盘中断急的多不是。所以他们决定给这类抢占别人的能力分个等级，（硬件）中断在interrupt level，在它之上还有N个等级，即使中断服务例程正在运行，这N个高等级的任务还是可以抢人家鸡蛋。完整的权限等级请自行google。&lt;/p&gt;&#xD;
&lt;p&gt;再来聊apc level和dpc level。这俩等级和体系结构没什么关系，纯粹是跟nt的实现机制有关的。apc提供了一个方法，能让一个线程侵入到别的线程，委托别的线程做一些自己想干的事情，比如进程要退出时，发起退出动作的那个线程会给进程里所有的线程都插一个apc，而该apc做的事情就是结束本线程。再比如上两回我们一直说的CompleteRoutine，也是用apc机制完成的。你可以把apc看成是类似*NIX信号的东东。dpc做的事情和apc有些相似，都是把某些工作委托给别人做，不过apc依附在线程上，而dpc是独立的，每个cpu都有一个dpc队列，线程调度前会先检查dpc队列，如果非空，会先做dpc再挑线程。这个概念和linux中的下半部很相似，大致想法就是中断服务例程越简短越好，其他事情弄个什么方式延后处理。nt内核中有一个时钟每隔一段时间interrupt一次并发出一个dpc，该dpc做的事情就是检测当前运行的线程是否已经用完了自己的时间片，如果用完了，则让它进入wait状态并挑个别的线程执行。所以我们看到，nt内核里的线程调度是运行在dpc级别的，倘若在dpc级别运行的东西要做sleep的动作，死锁就要发生了：我要睡觉了，我主动交出运行权--&amp;gt;调度器试图运行，发现它的级别也是dpc level--&amp;gt;抢占不了，同等级的人是不能相互抢占的。城管可以抢小商贩，但去抢拆迁队那还了得。--&amp;gt;you got a deadlock!。&lt;/p&gt;&#xD;
&lt;p&gt;总体来讲，passive level和interrupt level是显而易见的，也很好理解，apc level和dpc level则是nt实现机制引入的，其实看着烦人，但都很有用。再反过头来看，linux中真的没有IRQL的概念吗？未必，passive level和interrupt level是必然存在的，下半部可以看成是dpc，信号可以看成是apc，四个等级里前三者和nt内核一致，只有apc和信号有差别。所以我们说内核这个东西，发展思路已经日趋一致，正所谓大江大河汇入海，都一样。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/gussing/aggbug/1940196.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/gussing/archive/2011/01/20/1940196.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/gussing/archive/2011/01/19/1939095.html</id><title type="text">wdk tips (2): IO_STACK_LOCATION</title><summary type="text">如前文所述，nt内核的驱动模型没有完全使用函数调用栈，而是自己山寨出来一个IO_STACK_LOCATION，里面保存了驱动调用序列。我们知道函数调用栈的push和pop都是编译器帮忙弄的，你甚至都可以在完全不了解内幕的前提下写代码，但是驱动开发不一样，调用序列要你自己去关心，何时入栈，何时出栈，栈内保留的什么内容，全部都要照顾好，否则BSOD就在前方不远等你。与IO_STACK_LOCATION有关的函数有以下几个：IoSkipCurrentIrpStackLocation, IoSetNextIrpStackLocation, IoGetNextIrpStackLocation, IoCo</summary><published>2011-01-19T05:54:00Z</published><updated>2011-01-19T05:54:00Z</updated><author><name>gussing</name><uri>http://www.cnblogs.com/gussing/</uri></author><link rel="alternate" href="http://www.cnblogs.com/gussing/archive/2011/01/19/1939095.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/gussing/archive/2011/01/19/1939095.html"/><content type="html">&lt;p&gt;如前文所述，nt内核的驱动模型没有完全使用函数调用栈，而是自己山寨出来一个IO_STACK_LOCATION，里面保存了驱动调用序列。我们知道函数调用栈的push和pop都是编译器帮忙弄的，你甚至都可以在完全不了解内幕的前提下写代码，但是驱动开发不一样，调用序列要你自己去关心，何时入栈，何时出栈，栈内保留的什么内容，全部都要照顾好，否则BSOD就在前方不远等你。&lt;/p&gt;&#xD;
&lt;p&gt;与IO_STACK_LOCATION有关的函数有以下几个：IoSkipCurrentIrpStackLocation, IoSetNextIrpStackLocation, IoGetNextIrpStackLocation, IoCopyCurrentIrpStackLocationToNext，外加IoCallDriver等往下传irp的函数。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;IoCopyCurrentIrpStackLocationToNext&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;该函数将current location里的内容全部拷贝到next location中去，一般是你设置了CompleteRoutine之后会用。函数实现非常简单，用宏的形式存放在wdm.h中：&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;#define IoCopyCurrentIrpStackLocationToNext( Irp ) { \ &#xD;
    PIO_STACK_LOCATION __irpSp; \ &#xD;
    PIO_STACK_LOCATION __nextIrpSp; \ &#xD;
    __irpSp = IoGetCurrentIrpStackLocation( (Irp) ); \ &#xD;
    __nextIrpSp = IoGetNextIrpStackLocation( (Irp) ); \ &#xD;
    RtlCopyMemory( __nextIrpSp, __irpSp, FIELD_OFFSET(IO_STACK_LOCATION, CompletionRoutine)); \ &#xD;
    __nextIrpSp-&amp;gt;Control = 0; }&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;如上所示，其实就是一个RtlCopyMemory。一般在本层驱动需要CompleteRoutine时使用。常见的调用序列如下:&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;irpStack = IoGetCurrentIrpStackLocation(Irp);&#xD;
//&amp;hellip;your code with irpStack&#xD;
IoCopyCurrentIrpStackLocationToNext(Irp);&#xD;
IoSetCompletionRoutine( &#xD;
     Irp, &#xD;
     CompleteRoutine, &#xD;
     deviceExtension, &#xD;
     TRUE, &#xD;
     TRUE, &#xD;
     TRUE);&#xD;
IoCallDriver(deviceExtension-&amp;gt;nextLower, Irp);&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;注意，IoCallDriver会把stack location减一。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;IoSkipCurrentIrpStackLocation&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;该函数的实现就更简单了&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;#define IoSkipCurrentIrpStackLocation( Irp ) { \ &#xD;
    (Irp)-&amp;gt;CurrentLocation++; \ &#xD;
    (Irp)-&amp;gt;Tail.Overlay.CurrentStackLocation++; }&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;也就是把当前的location设置成上一层。该函数一般和IoCallDriver配合使用&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;IoSkipCurrentIrpStackLocation(Irp);//location+1&#xD;
IoCallDriver(deviceExtension-&amp;gt;nextLower, Irp);//location-1&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;执行完上面两步之后，location正好跟调用者一样，IO_STACK_LOCATION中的内容也不变。Filter driver常用此种手段转发irp：收到一个irp，获取或者修改其数据，继续转发，因为location没变所以上层驱动设置的CompleteRoutine依然会被filter之下的那个驱动call到，filter就跟透明一样。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;IoGetNextIrpStackLocation&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;该函数获取下一层location的指针&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;#define IoGetNextIrpStackLocation( Irp ) (\ &#xD;
    (Irp)-&amp;gt;Tail.Overlay.CurrentStackLocation - 1 )&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;获取该指针后可以设置IoControlCode等内容然后传给下层驱动处理，一般也和IoCallDriver配套使用&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;nextStack = IoGetNextIrpStackLocation(Irp);    &#xD;
nextStack-&amp;gt;MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; &#xD;
nextStack-&amp;gt;Parameters.DeviceIoControl.IoControlCode = code; &#xD;
nextStack-&amp;gt;Parameters.DeviceIoControl.OutputBufferLength = 0; &#xD;
nextStack-&amp;gt;Parameters.DeviceIoControl.InputBufferLength = 0;&#xD;
IoCallDriver(deviceExtension-&amp;gt;nextLower, Irp);&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;&lt;strong&gt;IoSetNextIrpStackLocation&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;我们重点来讲讲IoSetNextIrpStackLocation函数，该函数实现如下：&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;div &gt;&#xD;
&lt;pre &gt;#define IoSetNextIrpStackLocation( Irp ) { \ &#xD;
    (Irp)-&amp;gt;CurrentLocation++; \ &#xD;
    (Irp)-&amp;gt;Tail.Overlay.CurrentStackLocation&amp;mdash;; }&#xD;
&lt;/pre&gt;&#xD;
&lt;/div&gt;&#xD;
&lt;p&gt;与IoSkipCurrentIrpStackLocation正好相反，它把location设置成下一级。我们知道IoCallDriver等函数在转发irp的时候会帮我们把location减1，所以需要我们主动把location往下降的时刻很少见，一般是由于此种需求才会用到：你调用IoAllocateIrp生成一个irp，并且需要用到irp的current location。因为刚生成的irp的current location是不可用的所以必须主动往下降一级。正如wdk文档所说，这种需求是极其罕见的，原因有二：1. 大多数情况下你处理的irp是别人传给你的。2. 即使确实要生成一个新的irp，本层所需的数据也不需要存到current location中去，因为IoSetCompleteRoutine可以接受一个context域，每次CompleteRoutine被调到时，该context域也会一起返回给你，所有本层驱动所需的信息完全可以放在context中。但是我们都知道凡事都有个例外，其实还真就有需要用的IoSetNextIrpStackLocation的时刻，我们来看一个例子。&lt;/p&gt;&#xD;
&lt;p&gt;假设我们需要allocate一个新的irp并用同步的方法传递给下层驱动处理，我们先用IoAllocateIrp获取一个irp，设置相应的内容，然后调用IoGetNextIrpStackLocation获取下层location以便将control code设为IRP_MJ_INTERNAL_DEVICE_CONTROL，最后调用IoForwardIrpSynchronously函数同步完成irp。但是问题就来了：IoForwardIrpSynchronously函数内部会做一个IoCopyCurrentIrpStackLocationToNext动作，这个函数会把next location的内容替换成current location里的内容，所以我们设置的control code啥信息全被冲掉了。怎么办呢？既然它会用current的替换next的，那么我们直接把control code放在current里不就完了，IoForwardIrpSynchronously会把它拷到下一层去。愿望是美好的，但残酷的现实就是：irp是新生成的，还没有所谓的current location！这时候IoSkipCurrentIrpStackLocation就会显得很有用：先调用IoSkipCurrentIrpStackLocation将location下降，然后GetCurrentIrpStackLocation获取当前的location，设置control code，最后调用IoForwardIrpSynchronously同步完成irp。&lt;/p&gt;&#xD;
&lt;p&gt;看到这里也许你要发问了：为什么IoForwardIrpSynchronously要主动帮我们做copy to的动作，不是吃饱撑了嘛。问的好，答案就是：没办法只能这么做。我记得咱们说过，nt内核里的io全是异步的，上述所谓同步操作也都是用event模拟出来的：调完IoCallDriver后wait在一个event上，在CompleteRoutine里set event。返回到前面对IoCopyCurrentIrpStackLocationToNext的描述我们知道，既然要用到CompleteRoutine，那么IoCopyCurrentIrpStackLocationToNext操作就是免不的了。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;update:&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;另有一种情况也会有用到IoSetNextIrpStackLocation。当你allocate了一个irp，set了CompleteRoutine，调了IoCallDriver，一切都完成等complete时,问题就来了：你设置的CompleteRoutine被调到时传入的DeviceObject为NULL。Why？Because CompleteRoutine是设置在next location中的，而DeviceObject保存在current location中。&#xD;
当一个irp被create出来之后，它的current location是无效值，所以没地方存放DeivceObject。怎么办？按上面所述方法，借助IoSetNextIrpStackLocation完成。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/gussing/aggbug/1939095.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/gussing/archive/2011/01/19/1939095.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/gussing/archive/2011/01/18/1938140.html</id><title type="text">wdk tips (1): threaded irp和non-threaded irp</title><summary type="text">nt内核的IO模型中，IRP有两类：threaded irp和non-threaded irp，顾名思义，前者跟thread绑定，后者跟thread无关。当一个threaded irp被创建时，创建线程会有一个队列保存该irp，直到irp完成之后才释放。当你试图让这条线程退出时，系统会检测队列看里面是否还有irp没完成，如果有，线程会一直等待，直到所有的irp全部完成。而non-thread irp则正好相反，如果该irp已经返回到了创建它的地方你还继续complete它，BSOD将会发生。Threaded IRP如前面所讲，threaded irp和线程绑定在一起。当user mode程序发</summary><published>2011-01-18T03:32:00Z</published><updated>2011-01-18T03:32:00Z</updated><author><name>gussing</name><uri>http://www.cnblogs.com/gussing/</uri></author><link rel="alternate" href="http://www.cnblogs.com/gussing/archive/2011/01/18/1938140.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/gussing/archive/2011/01/18/1938140.html"/><content type="html">&lt;p&gt;nt内核的IO模型中，IRP有两类：threaded irp和non-threaded irp，顾名思义，前者跟thread绑定，后者跟thread无关。当一个threaded irp被创建时，创建线程会有一个队列保存该irp，直到irp完成之后才释放。当你试图让这条线程退出时，系统会检测队列看里面是否还有irp没完成，如果有，线程会一直等待，直到所有的irp全部完成。而non-thread irp则正好相反，如果该irp已经返回到了创建它的地方你还继续complete它，BSOD将会发生。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;Threaded IRP&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;如前面所讲，threaded irp和线程绑定在一起。当user mode程序发起IO操作（比如WriteFile函数），或者一个驱动向另一个驱动发起IO操作（比如ZwWriteFile），IOManager首先找到文件handle对应的驱动栈，获取栈顶的PDEVICE_OBJECT，生成一个irp，将IO操作的各种信息放入irp中，并将此irp放入线程的irp队列中，然后调用IoCallDriver将irp转给驱动栈一层层处理，每往下一层，IO_STACK_LOCATION中的指针就会往下移一个位置（实际情况比这稍微复杂一点点，以后再说，现在先把它想成是一个栈，每往下一层就是push一次），直到最后跟硬件打完交道返回。返回的过程正好与上述相反。最末一层处理完irp后，会调用IoCompleteRequest，此函数会将IO_STACK_LOCATION指针往上移（pop），并最后调用上层驱动设置的CompletRoutine。每层的CompleteRoutine做完相应工作后都会调用IoCompleteRequest讲irp继续往上转，直到栈顶PDEVICE_OBJECT。此时IO_STACK_LOCATION已经在顶部，所谓再上层的CompleteRoutine也不存在，所以本层CompleteRoutine中操作将irp归还给IOManager，IOManager负责回收资源，并从线程irp队列中去除相应的项。&lt;/p&gt;&#xD;
&lt;p&gt;这个过程和函数调用很像，像到你都会发问：为什么搞成这种模式，函数调用和返回不也有压栈出栈的操作吗，干嘛不借用这套机制反而要另起炉灶单独搞一套。答案很简单，就是效率。nt内核的IO操作全是异步的，调用IoCallDriver的线程和CompleteRoutine响应到的线程，不一定是同一条。关于这点我们以后再谈，但它却引出了一个目前我们就需要关心的问题：既然CompleteRotine的线程环境可能已经切换了，那么IOManager如何知道从哪个线程的irp队列里去除相应项？答案也很简单，就是保存在irp中。每一个threaded irp的PIRP-&amp;gt;Thread域都保存它的创建线程，IOManager就是用它来寻找线程。注意：有人会想从PIRP-&amp;gt;Thread里获得当前线程的信息，这是不对的。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;Non-threaded irp&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;non-threaded irp一般都由驱动创建而不是IOManager，线程不在irp队列里保存它的实例，并且它的PIRP-&amp;gt;Thread为NULL。当CompleteRoutine一路返回调到该irp的创建者时，千万不要返还给IOManager，直接free掉就可以了。&lt;/p&gt;&#xD;
&lt;p&gt;&lt;strong&gt;irp创建函数&lt;/strong&gt;&lt;/p&gt;&#xD;
&lt;p&gt;从user mode一路下来的irp一定是threaded irp这很明显。那么内核态的函数中，哪些是创建threaded irp，哪些是创建non-threaed irp的？以下表格列出了各函数：&lt;/p&gt;&#xD;
&lt;table cellspacing="0" cellpadding="2" width="482" border="0"&gt;&#xD;
&lt;tbody&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td valign="top" width="280"&gt;non-threaded&lt;/td&gt;&#xD;
&lt;td valign="top" width="200"&gt;threaded&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td valign="top" width="280"&gt;IoAllocateIrp&lt;/td&gt;&#xD;
&lt;td valign="top" width="200"&gt;IoBuildSynchronousFsdRequest &lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td valign="top" width="280"&gt;IoBuildAsynchronousFsdRequest&lt;/td&gt;&#xD;
&lt;td valign="top" width="200"&gt;IoBuildDeviceIoControlRequest&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;tr&gt;&#xD;
&lt;td valign="top" width="280"&gt;&amp;nbsp;&lt;/td&gt;&#xD;
&lt;td valign="top" width="200"&gt;TdiBuildInternalDeviceControlIrp&lt;/td&gt;&#xD;
&lt;/tr&gt;&#xD;
&lt;/tbody&gt;&#xD;
&lt;/table&gt;&#xD;
&lt;p&gt;这里有个特例：IoMakeAssociatedIrp， 它是non-threaded irp,但是你不能直接free掉它，还是要调用IoCompleteRequest，让它的主管irp去释放。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/gussing/aggbug/1938140.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/gussing/archive/2011/01/18/1938140.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
