每天我们都在使用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
分析一个软件,最easy的入手方法就是先搞清楚它的每一个文件都是干嘛的。我们先来让大家对自己正在使用的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开发函数,经过几次封装后供开发人员使用。我们先无论这些函数的功能分类,按功能分类的话东西有点多有点乱,easy让人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的朋友有所帮助,起一个抛砖引玉的作用,有对此感兴趣的朋友能够交流下。仅仅可惜此篇文章没有找全,非常是遗憾,有朋友找到的请通知一声。。再次感谢下该文作者。。