每天我们都在使用Windows系统学习、编程、听音乐、玩游戏,Windows的操作想来是很熟练了,可是你又对Windows到底了解多少呢?本系列的目的,就是让你对Windows系统有个更直观、更清楚、更彻底的认识。虽然我们大多数人看不到Windows的源码,对其内存调度算法这种最深层次的技术内幕不能明窥,但是我们可以做到比现在知道的更多,了解这些之后你会发现在Windows上面开发会轻车熟路,任何木马病毒到了你机器上不过只会成为你的试验品。
鉴于Windows 9X内核早已淘汰,技术过时,在此不予讨论。主要是针对Windows2000(Windows 5.0)以后版本,尤以2000为主。要知道xp是Windows 5.1版本,2003也才是5.2版本。那么,对于本系列提到Windows OS技术,统指Windows 5.X技术。
分析一个软件,最容易的入手方法就是先搞清楚它的每个文件都是干嘛的。我们先来让大家对自己正在使用的Windows 操作系统有个直观认识。
请在你使用的Windows 2000或Windows XP上,于WINNT/system32或Windows/system32目录(系统目录)下,找到以下几个文件:HAL.DLL、NTOSKRNL.EXE、NTDLL.DLL、KERNEL32.dll、GDI32.DLL、USER32.DLL。这几个是Windows系统赖以执行的关键,只要你能在图形界面下操作Windows,这几个文件就肯定在发挥作用。值得一提的是Windows的图形界面子系统是在内核实现的
(知道为什么Linux的图形界面怎么着都没Windows好看好用了吧)。
对这几个文件的功用给出简介如下:
HAL.DLL:Hardware Abstraction Layer,硬件抽象层。 Windows 2000也是一个软件系统,与我们平时从事的项目开发工作一样,项目初期需要确定开发目标。
而Windows2000开发当初制定的开发目标之一就是要能够在多种硬件平台上移植(Windows2000支持单处理器、多处理器、Compaq SystemPro),不同的硬件平台上有不同的硬件抽象层实现。在我们通常使用的标准PC上这个文件就是HAL.DLL(在Compaq SystemPro上是HALSP.DLL,满足不同硬件平台的硬件抽象层文件在Windows安装盘上都有,安装时根据计算机的平台类型只复制相应的文件)。 HAL是可加载的内核模式模块(DLL动态链接库文件),它为Windows系统所运行的硬件平台提供低级接口。它隐藏硬件相关的细节,如I/O接口、中断控制器和多处理器通信机制等这些与特定硬件结构机密相关的内容,而向操作系统内核提供统一的硬件接口函数。
NTOSKRNL.EXE:NT OS Kernel,毋庸置疑,这就是Windows系统的内核,确切说内核是在这里面实现的,只有1.6兆,与目前Linux2.4版本内核编译后的大小差不多。
这个文件实际上提供两部分的主要的功能:
一是系统内核,这在硬件抽象层之上,提供系统的基本机制(线程调度和同步、内存分配等等一切你在操作系统原理书上可以看到的最复杂的那一部分,如果你觉得操作系统课程学得太抽象又有点跟自己过不去,试着把这个NTOSKRNL.EXE反汇编了读一读吧,不多,也就300多万行汇编码,只是找不着确切的入口点)。
同时内核还提供硬件支持,实际上是将驱动程序等上层程序的调用对硬件进行具体化,反过来讲就是硬件抽象层再抽象一次,暴露给上层一个统一的接口,写过Windows驱动程序或是看过DDK的同学一定对Windows WDM驱动的固定编写模式有很深的印象。
二是执行程序(称为Executive)。这个执行程序的东东可真是多啊,我在捣腾的时候都有点让它搞得晕晕的,不过现在来看呢其实也就一句话:
Windows之间交互的窗口,再直观点说执行程序对外暴露Windows开发函数,经过几次封装后供开发者使用。我们先不管这些函数的功能分类,按功能分类的话东西有点多有点乱,容易让人puzzled。按开发者的角度来看,有这么几类:Win32 API函数,这是做应用开发用的,我们常说的MFC、ATL是对其进行的面向对象化及封装;DDK API函数和IFS kit函数,这是供驱动开发用的,DDK还耳熟些,不过如果你浏览过DDK并且够细心的话就会觉得奇怪为啥OS很重要的一部分——文件系统驱动的开发没有提供,IFS(Installable File System) Kit就是做这个的啦。
我们前面说过Windows把图形界面处理放在了内核态来执行,因此不得不提一个比较特殊的驱动:Win32k.sys,你看到的漂亮的Windows桌面就是这个文件画出来的。
讲到这里,前述内容是Windows系统在内核态运行的基本功能,也就是说,这是运行于CPU ring0级别的(不懂什么是ring的话赶紧去Intel的网站download Intel CPU的系统开发参考看,甭翻你手头的微机原理课本了,那上面没有)。
还有一大堆驱动也是运行在这个级别。在内核级别上,组件之间功能的调用等等与用户态不同,常用的是LPC,这个我们以后慢慢再讲。
继续下面的内容之前,要先说明一点事实:Windows的功能比我们平时所见的要强大的多。通常我们讲Win32,实际上这只是Windows的一个子系统而已,Windows还有另外的两个子系统:POSIX和OS/2。这么一说的话似乎Linux上的程序可以在Windows上运行了,其实没那么简单,还需要移植库以及重新编译连接,最终还得依赖Win32子系统的实现来完成其功能,意义不大,基本上算是被用户们喀嚓掉了吧,我们也是只讲Win32子系统。
点明一点:不同的子系统有专门的子系统支持环境,POSIX子系统是POSIX.EXE,Win32子系统是Csrss.EXE(全称是Client/Server Run-time SubSystem)
。所以,你的机器上总有Csrss.exe这样一个进程在运行,别无聊的以为它是木马没事就Kill着玩。
下面是用户级别的内容。为了方便理解,内核级别的分层介绍我们是从下往上(由硬到软)开始,在用户级别,我们从上往下开始介绍,这样更直观些。
我们先举一个应用程序的例子(尽管用户级别的程序远不止应用程序这么多),从IE讲起。如果你装了VC,并且顺带安装了Depends这个工具,用它打开IE(或是任何一个windows可执行文件都可),你会发现IE主程序(IEXPLORE.EXE)调用了Kernel32.DLL,而Kernel32.DLL又调用了NTDLL.DLL,同时,IE主程序还调用了USER32.DLL,USER32.DLL又有对GDI32.DLL、KERNEL32.DLL、NTDLL.DLL的调用,期间还有循环调用。
去繁从简,Win32应用程序要调用Win32 API函数,这些函数正是有KERNEL32.DLL提供的,而KERNEL32.DLL这个文件其实并不实现具体的功能,只是做了一个简单的地址指针转换,把函数入口点又跳到NTDLL.DLL里去了,对应到了相应的本机API(NATIVE API)函数上,NTDLL.DLL也不做具体的处理,通过系统功能调用将用户级的函数调用转换成内核模式的真正的系统功能调用,由内核执行完毕后返回应用程序进程。
也许有人要问Windows为什么要通过Kernel32.DLL和NTDLL.DLL对执行程序Executive暴露的编程接口做两次封装,其实这也是Win32 API和NATIVE API的区别。
Win32 API又称为存档的API,是供用户使用的,必须保持一致性和兼容性,不能随随便便修改函数命名,新函数的增加必须保持对老函数的兼容,否则使用老版本的Win32 API开发(直观的就是我们使用win32 SDK或MFC开发)的程序在新版本的系统上可能就要运行出错。
而NATIVE API则是在系统更新时可能需要进行修改的函数,比如函数名、函数参数的类型和个数等,都有可能随着系统升级而变更,因此是留给MS自己人来用的,当然不能直接给用户使用。但这不是说就我们作为开发者就不能使用NATIVE API,如果你自己定位NTDLL.DLL中的函数并进行调用,只要这个函数未被MS修改,可以肯定这种调用是成功的,你的程序运行也没有任何问题。与KERNEL32.DLL类似的还有一个ADVAPI32.DLL,提供一些比较高级的应用编程函数。
GDI32.DLL和USER32.DLL提供存档的Win32图形编程接口,它们也是通过NTDLL.DLL来完成对系统绘图功能(在win32k.sys中实现)的调用。
KERNEL32.DLL、ADVAPI32.DLL、USER32.DLL和GDI32.DLL统称为Win32子系统DLL。Win32子系统DLL将存档的Win32 API函数“翻译”为相应的对本机API函数调用,NTDLL.DLL将本机API“翻译”为NTOSKRNL.EXE和Win32k.sys的内核模式系统服务调用,来完成用户级别功能需求的实现。
讲过应用程序之后,再看看用户级别的其它程序。环境子系统(Win32子系统、POSIX子系统、OS/2子系统),这个前面提过了;服务进程,也就是你在服务管理器里可以看到的那一堆东东,包括Services.EXE这个比较特殊的服务程序、svchost.exe等等,这些以后慢慢谈;再就是系统支持进程,如SMSS.EXE(会话管理器)、WINLOGON.EXE(登陆程序)、LSASS.EXE(本机安全权限子系统)等,这些都是专门的内容,有机会将做专题研究。
在这里提到的进程都是Windows系统正常运行必要的一些进程,也就是说它们是安全的,大家不必对其有疑心(当然了,有漏洞那是例外)。
大部分用户级别应用程序的执行要调用KERNEL32.DLL,然后再间接调用NTDLL.DLL,系统支持进程等程序有的是直接调用NTDLL.DLL。
系统机制:
windows2000为执行体、内核、设备驱动程序等核心态部分提供了一些基础机制。先让我们看看都有哪些:
(1)陷阱调度:包括中断、延迟过程调用(DPC)、异步过程调用(APC)、异常处理、系统服务调度。
(2)执行体对象管理
(3)同步机制:自旋锁、内核调度对象以及等待是如何实现的
(4)系统线程
(5)多种系统机制如windows2000全局标志
(6)本地过程调用
下面我们就来逐个分析:
陷阱调度:
中断和异常的实质是使CPU不按照正常的步骤来工作,硬件和软件都能够察觉到它们。陷阱是当异常或者中断发生时能够保存当前线程状态并转向相应处理的一种系统机制。在windows2000中,处理器将控制交给陷阱处理程序--一些专门来处理中断、异常的程序。
内核通过以下方式来分辨中断和异常:中断是一个异步事件(可以在任何时间产生),不管处理器在执行什么程序。中断典型地由I/O设备、时钟、定时器产生,必要时我们可以屏蔽中断。而异常是一个同步事件,它是由正在执行的特定代码产生的,重新执行相同的代码会重复产生特定的异常。比如访问非法内存、除数为0等。
系统把系统服务也作为异常来处理。
无论是硬件或者是软件都能产生中断和异常,比如说,一个总线异常是由硬件引起的,而除数为0显然是软件中的BUG导致的;同样,I/O设备可以产生中断,内核本身也能产生软中断(APC,DPC)。
当一个硬件中断或者异常发生时,处理器收集足够的状态信息以保证当异常或中断处理完毕后可以正确返回到当前执行点。
处理器通过在当前线程的内核栈区建立一个陷阱框架(用来保存现场)来实现。陷阱框架通常时线程整个上下文环境的一部分。而把软中断当成硬中断的一部分来处理,或者是调用内核中相应的处理程序。
在大多数情况下,当有陷阱发生时内核负责寻找相应的处理程序并且在处理程序返回时负责恢复中断线程的继续执行。
中断调度:
硬件中断是由I/O设备产生以求获得CPU服务的,这种中断机制使得CPU的利用率提高很多。软件也能够产生中断,比如说,内核可以发起一个中断来进行线程调度。
内核在必要的时候可以关中断,这样CPU就可以屏蔽掉任何中断-----这在有些时候是必要的,比如线程对于临界区的访问,异常处理等。
硬件中断处理:
在X86家族里,外部中断在中断控制器里进行排队,控制器依次中断CPU的运行。当CPU被中断时,它要求中断控制器提交中断请求,中断控制器将中断请求翻译为中断请求号,并且把这个号码当成索引来查询中断调度表(IDT),并且将控制权交给相应的中断处理程序。
中断调度表(IDT)在系统启动的时候就已经初始化了,里面包括中断号和相应处理程序的对应。
中断请求登等级IRQL:
中断是有等级的,这对于软、硬中断都是适用。内核将中断为0---31共32个等级,等级高的权限高。内核负责将软中断对应到相应的等级上,HAL负责将硬件中断对应到相应的等级上。如下图所示:
中断处理是按等级来运行的,并且是抢占式的,高等级的可以阻塞低等级的运行。当运行中断处理程序时,CPU把自己的IRQL设置为要执行的中断的IRQL,然后运行.
那么系统是如何把硬件中断映射到相应的IRQL的呢?答案是HAL,在WINDOWS2000中,总线驱动能够得知所有连接到总线上的设备,
以及每个设备能发出何种中断,紧接着总线驱动把所得到的情况汇报给PNP(即插即用管理器),它做出抉择。
最后调用HAL函数HalpGetSystemInterruptVector来实现映射。
注:该文章是认识Windows系统内核的入们文章,转载至此希望对想深入学习Windows的朋友有所帮助,起一个抛砖引玉的作用,有对此感兴趣的朋友可以交流下。只可惜此篇文章没有找全,很是遗憾,有朋友找到的请通知一声。。再次感谢下该文作者。。