对Windows和类Unix争论的观点] 谁是谁的躯壳,谁又是谁的灵魂
虽然不一定是完全正确, 但还是有理有据的认真写的........ 我觉得有一定价值, 转来了
-----------------------------------------------原文---------------------------------------------
/*夹带私活技术文,约2万字容量,慎入*/
/*写作的起因大概是之前有一篇据称是清华申请退学的博士写的一篇力推Unix和一系列类Unix系统的文章,迅速得到一片叫好。粗看文章,批判Windows批判的句句在理,对Unix理念和Unix哲学极致推崇,但是细看,发现作者完全不了解Windows,也懒得了解Windows,更不要说其上面的软件了,连MSDN上有详细阐述的内容都不加以查证、甚至操作系统最基本的基础知识掌握的都不是很扎实,当然最大的问题,就是以个人或者说是一群学院派的观点出发强奸整个产业界和全球数十亿计算机使用者。所以我强烈怀疑那是一篇对清华CS的高级黑文章,当然本文不是为批判那篇文章而做,只是正本清源,跳出学院派和社区的视点,从历史、技术、产品、设计的角度透视Windows及其他Microsoft的产品,然后谈谈哲学什么的。*/
首先说,Windows操作系统并不是世界上第一款被广泛应用的操作系统,在其之前,AT&T的Unix已经大行其道,并且在AT&T和UC Berkeley之间那场旷日持久的诉讼之后成功的转型为一套开源操作系统,当然,当时Unix并没有走出实验室,因为这款诞生于实验室的操作系统的设计初衷就是要建造一款与机器无关的实验室用操作系统,因此其从底层实现到架构到用户层都完全按照实验室系统的要求进行设计和实现的,在Unix出现后十年间,Unix及其他由Unix系统衍生出的操作系统都只限于实验室和爱好者之间流行,无法真正的走出实验室。Microsoft是在这样一个环境下诞生,Bill Gates创建它时的想法是“要让全世界每一个家庭的桌面上都有一台计算机”。
对比AT&T和Microsoft的宗旨,我们不难发现,在AT&T的贝尔实验室这个挤满各种专家的地方,他们更多的关注的是实验室和科学家的使用,Microsoft关注的则是如何让计算机走入家庭,在这个建造原点的分歧,直接导致了两大体系日后完全不同的道路、完全不同的表现和社会评价。
在Unix的版本不断演化、架构不断适应其需要的专业群体使用的特征的时候,Microsoft则开始构建一套家用操作系统,在构造之处,Microsoft即意识到一款操作系统如果想走出实验室就必须要降低使用的技术门槛。Unix和类Unix操作系统历来不被用户接受但又在爱好者中好评如潮的原因就在于其入门的技术门槛太高,任何新接触Unix和类Unix操作系统的用户都需要一段时间的学习和适应才能在其上实现一些常用操作,如果想要达到熟练使用的地步,就必须投入大量的时间和精力去学习,有一句话是“Unix是用户友好的,但是成为它的用户,是极其困难和痛苦的”。也正是出于对Unix这样的特点的校正,Microsoft最初开发的几版DOS,其命令行体系较Unix和类Unix系统而言要简单易学很多,尽管这套系统在很多社区看来并不是十分的pro。之后Microsoft开始引入GUI(图形界面)这种曾经在学院派和爱好者们眼中十分不pro的东西,并且借助GUI的简单易用开始占领家庭的桌面,把计算机从实验室和企业机房带到了家庭的桌面。
同时,Microsoft在和IBM共同推出PC机的过程,开始关注到贝尔实验室的这帮学院派和社区的爱好者们永远也想不到也永远不会去想的事,也就是作为一款操作系统的生态系统。Microsoft在之后构建的操作系统开始向平台化发展,为了简化图形界面程序的开发过程并统一Windows应用程序的视觉风格,Microsoft开始为开发者提供封装好的基础类及其配套的编译工具,这就是MFC(Microsoft Foundation Class)、WCF(Windows Communication Foundation)、WPF(Windows Presentation Foundation)甚至是C#和Microsoft Visual Studio的由来。
Microsoft从诞生的第一天起就从没有想过要做出一件很pro很时髦的东西出来,在Microsoft所有可查阅的文件中,我们随处可见的只有“稳定”“性能”“易用”等几个词眼而已,在这样的指导思想之下,Microsoft的产品风格与学院风格越走越远。另外,由于Microsoft是一个极度倡导知识产权保护的以商业为主的企业(事实上现代有关计算机知识产权的诸多国际条款和法律都与Microsoft和Bill Gates有着极其密切的联系,软件、设备厂商不提供源代码的行业惯例也是Microsoft开创的),在高等院校师生和ISO、IEEE的人看来,追随Microsoft的技术是一件向商业妥协的没有学术品位的事情,因此,在各项标准的制定过程中,几乎都是绕着Microsoft走的,凡是Microsoft设计的,无论好坏都不采用为标准,新出的ISO一定是对之前Microsoft的否定,这在学术界甚至成为了潜规则,如果你想让你的东西成为国际标准,只要你跟Microsoft对着干就好了。这样的态度即便到了现在,依然没有改变的趋势,尽管Microsoft的OOXML文本标准(就是Microsoft Office 2007格式)被采用为国际标准,但这项标准实际上受到了除Microsoft和金山以外所有Office厂商的集体抵制,原因很简单,因为它来自Microsoft。
前面说了这么多无非是希望大家可以放下固有的观点来从一个科学的角度来分析和评价一款操作系统,而不是人云亦云的跟着骂几句就完事了的。
架构、架构还是架构
Microsoft Windows从1.0走到现在的6.1已经走过了17个年头,其间共发行过基于MS-DOS的Windows操作系统1.X、2.X、3.X和9X以及基于WinNT架构的5.X和6.X六代操作系统。Microsoft真正的开始独步操作系统市场是在1995年Windows 95操作系统的上市开始的。这代操作系统堪称Microsoft的一次经典,在其所有的竞争对手都还沉浸在把16位计算技术做到极致的梦幻之中的时候Microsoft Research系统已经历时三年研究并成功产品化了32位计算技术,除此之外,Microsoft重新设计了GUI使得GUI更加符合用户的自然习惯,并且得益于Microsoft的版本统一,Windows系统的生态系统得以建立在单一的运行环境基础之上,围绕Windows的生态系统得以迅速产生并发展成熟,于是Microsoft借助Win95一举登上了软件界的霸主地位,随后5.X的开山之作Windows 2000亦是一次彻底的革命,将操作系统内核从传统的宏内核转变成了混合内核架构,使得Windows在性能不明显降低的前提下稳定性得到了本质的提升,也直接奠定了现代Windows操作系统的框架基础。
现在再探讨古老的MS-DOS架构是没有意义的一件事情,因此,下面的所有讨论都是基于WinNT架构的,并且会以现在流行的WinNT6.X架构,兼顾X86和X64两种体系结构。
那些有关内核的故事
前面说过WinNT架构是不同于MS-DOS的宏内核架构的混合内核架构,那就要先分辨什么是宏内核和微内核。
宏内核架构与微内核架构最大的区别在于操作系统的具体功能的实现级别,在宏内核的架构下,整个操作系统的底层被设计成一个程序,包括进程分配、内存管理、多任务切换、I/O管理等一系列功能都被写入同一个程序,操作系统在执行对应的任务的时候通过程序内的函数调用予以实现,由于函数调用的开销要比进程间通信和调用小得多,因此,基于此模式的操作系统的内核性能非常好,但是,这样的框架决定了所有的系统功能以及驱动程序都运行在同一段内存空间内,因此只要任何一处出现异常,比如第三方驱动失去响应,整个操作系统内核就会崩溃,这也就是为什么早期的Win9X系列蓝屏比较频繁的缘故。
严格的微内核架构则将内核的功能缩减到最小,仅保留内存管理、多任务支持、进程分配等基础核心操作,除此之外I/O管理、网络服务等并不是所有任务都需要的内核支持被以系统服务的形式运行于内核之上。这种模式的好处就是内核极其易于维护并且几乎不会因为第三方驱动程序或内核组建发生错误而崩溃的现象,但是由于操作系统的底层操作之间的交互都是通过进程间通信和程序调用实现的,这必然导致操作系统的性能低下。
于是现在的某些操作系统,如Unix系列,采用的是宏内核模式,当然,由于针对Unix的硬件驱动十分稀少而且开发周期远比Windows平台要长得多,Unix这种天生的稳定性的致命缺陷暂时还没有显露出来。
而WinNT以及以Linux为代表的类Unix系统这两个体系的操作系统则采用了较为特殊的混合式内核模式,即将传统意义上的操作系统内核层与实际的内核剥离,并在内核层与硬件层之间加入一层硬件抽象层,用以隔离软件与硬件环境,其上有一个微内核层,将负责进程分配、多任务支持、内存管理等基本内核任务的微内核置于该层,在其上放置多个宏内核,这些内核分别为进程宿主、本地服务宿主和网络服务宿主等支持上层平台的基本服务以及第三方驱动程序宿主,第三方或系统服务以及驱动程序均以注入的形式被操作系统加载并使用,相互密切关联的服务或驱动均会被同一个程序加载。
基于这样的内核架构,无论是Windows还是Linux都提供了与Unix接近的性能(毕竟Unix的完全宏内核模式是性能最优秀的架构)并提供了远高于Unix的稳定性。当然,由于Windows的流行度很广,驱动和服务程序数目庞大,整个生态系统市场竞争环境导致各类软件的开发周期被最大限度缩短,导致被安装进操作系统的服务和驱动并不都是那么的可靠,但又由于这种混合内核架构的稳定性保障,导致Windows操作系统在某些同学手里尽管很少崩溃,但时常大姨妈。由内核层面来看,Unix和Linux等操作系统并不比Windows更稳定,现在所观测到的现象而完全是由于系统中安装的第三方软件的数量差异导致的观测偏差而已,当你给Linux或Unix安装与你的Windows同等数量的第三方驱动和服务程序的时候,或者当Unix或Linux系统的流行程度达到Windows的程度时,你的系统只可能比Windows更不稳定。
当然,WinNT内核的架构和Linux内核结构也并不是完全一致,存在一些差异,也正是这些差异直接决定了WinNT和Linux的不同表现。虽然同样以混合内核架构搭建内核,但由于Windows完全由一家商业公司维护,因此WinNT内核的设计可以与上层需求紧密结合而无需考虑上层环境的可替换性。WinNT内核中的内核层宿主里较Linux内核提供了更多的支持,比如用户态驱动程序的支持、Win32桌面环境的支持、Win32 API的支持,在X64体系结构下的WinNT还会加入WoW6432Node的支持用以支持在X64平台上同时运行64位和32位应用程序,这些东西在Linux系统之下则完全是由上层环境实现的。另外,由于Linux操作系统的开源特性,任何人都有权查看和修改Linux的源代码,因此,目前Linux支持的设备中除了几个类别的设备以外,其余所有设备的驱动程序均被直接编译进Linux内核,这个特性是继承自早期的Unix系统,当然,现在的Unix和Linux内核也允许设备驱动以插件形式(即Windows的硬件支持模式)存在。不过Unix类系统中有一个发行版本比较例外,即MacOS。由于MacOS基于较为陈旧版本的Unix发行版(过时版本的FreeBSD),MacOS的内核并不支持这种可扩展的驱动程序。
壁垒森严的内存世界
详细去探究操作系统的内存管理模式是一件很惊心动魄的事情,一般人不敢在自己的计算机上轻易地尝试,另外,对于现代成熟的操作系统来说,内存保护的花样层出不穷,强行从外部探查整个系统的底层程序分布几乎是不可能完成的任务,不过好在Microsoft在MSDN和Windows SDK手册里给出了详细的WinNT内核内存管理资料,至于Unix和Linux,虽然没有这样官方的、完整的说明,但毕竟代码是可以查看的……
在WinNT的内核管理下,Windows的内存被划分成若干块相互隔离的空间,每一个独立的进程使用一个内存空间,并无权访问其他内存空间,一旦发生越界操作,系统即会终止问题程序。在这样的隔离环境之下,WinNT内核还会监控内存空间的指令段和数据段,一旦在指令段发现数据段的数据,即会终止程序的执行(即DEP),在WinNT操作系统中,如果底层的硬件不支持DEP,硬件抽象层中的微内核会提供这样的保护,如果底层硬件支持,则直接由硬件来负责监控,内核只负责捕捉到异常后的处理操作。
WinNT除了提供传统的DEP保护之外,还提供了其他内存保护机制。虽然DEP是每一个操作系统都具备的基本内存保障功能,但是后面说到的几项技术只有WinNT上面才有,有一些还是在WinNT6.0之后才被引入的,引入的原因很简单,就是因为WinNT占据了将近90%的市场份额,各种针对WinNT的攻击和安全威胁层出不穷,在这样严峻的安全形势下,Microsoft不得不不断引入这些技术以保证内存层面的安全。
在WinNT中,除了DEP技术之外,还会通过将系统可执行程序随机装载到内存里,从而防止缓冲溢出攻击(ASLR)。如果一个动态链接库(DLL)文件的动态重定位标志设置为真,那么它就会自动地随机装载到内存中。那些在特定内存区域寻找特定文件的恶意软件就会失效不能再li利用漏洞。实际上ASLR还能通过让那些正受攻击的系统文件崩溃来误导恶意软件。 WinNT还会检测程序栈中的所有SHE结构链表,特别是最后一个SHE结构(拥有一个特殊的异常处理函数指针)向其中插入一个硬地址,这样通常覆盖SEH后为了稳定溢出而使用的payload就会破坏掉SEH链,从而导致SEH链找不到末尾的地址于是系统就会认为SEH被覆盖了,从而进程被终止(SEHOP)。由于当前绝大部分的Shellcode在运行时都需要搜索要用到的API地址,因此,WinNT内核会监控对API地址的枚举行为,一旦发现枚举行为就终止程序执行(EAF)。另外,WinNT内核在初始化任何内存空间的时候均会将常见的内存弱点预先分配掉并且会为NULL指针所实际指向的目标地址预先分配一段数据,用以阻止指针无意或有意的使用不当造成的安全问题。
从内存层面讲,WinNT提供的安全性和可靠性要远远高于Unix及其它类Unix操作系统,由于Unix和类Unix操作系统并不需要面对每天新诞生的数以千万计的恶意软件或者攻击,Unix和类Unix系统的内存层面并未添加如此众多的复杂的保护措施,使得这两大类操作系统在面对这类在Windows平台上可以被轻松化解的威胁时毫无招架之力,同样,WinNT6.X提供的安全性和可靠性要远远高于WinNT5.X。
神秘的内存管理策略
Windows的内存管理机制一直为Unix和类Unix系统爱好者所诟病,一部分人觉得Windows将大量内存空闲的同时将每一个应用程序的内存都按活动概率从低到高依次压到硬盘中的缓冲区去,造成了内存资源的浪费,另一部分人则认为Windows在运行一些程序后并不完全释放其占用的内存,是一种内存管理无能的表现。
仔细研究Microsoft给出的关于内存管理机制的说明发现上述被大多数Unix和类Unix系统爱好者大肆批判的表现其实是Microsoft精心研究和设计过的。通过Microsoft Research的研究和测试发现,由于应用程序的生命周期的不同,内存空间的占用时间长短差异很大,当操作系统持续运行一段时间后,由于各个应用程序的空间在其生命周期结束后会被自动释放,导致整个内存不连续且间断空间大小不一,内存中的空间分布就像一块蓬松的蛋糕一样断断续续(这种情况也可以被称为内存碎片化),当应用程序所需要的内存资源较大(通常是在内存中已经找不到一块连续的区域能够存放下整个应用程序内存空间)的时候,应用程序的内存空间在物理上的分布并不连续,而操作系统提供的应用程序内存视图又必须连续,因此,操作系统需要花费大量的开销用以为应用程序内存寻址提供底层支持,这个开销导致的性能损失要远高于内存与硬盘数据交换造成的损失。依据Microsoft Research的长期实验结果,无论是计算机的内存空间大小,当内存占用量超过50%时,这种严重碎片化和性能直线下降的概率变得十分的高。
为了减少这种性能损失极高的操作出现的概率,Microsoft决定从根本上改变内存的管理机制,使得内存的使用量尽量远离这个性能急速下降的临界点,解决的方法即在不影响或不显著影响性能的前提下,尽可能少的使用物理内存空间。在WinNT操作系统中,内存被以页的形式管理,一个内存页为一个基本管理单元,每一个内存页的大小为4K,每一个应用程序的内存空间由很多个内存页组成,WinNT系统内核会监视内存页的存取操作,自动将内存中使用概率低的内存页写入硬盘上的页面文件(pagefile)里,当CPU访问这些被写入虚拟内存的内存页时,CPU会返回一个名为“硬错误”的信息,此时,操作系统会将相应的内存页从页面文件中读入内存,此即Windows下的虚拟内存机制(Linux下有与之类似的Swap机制,不过Swap机制主要的用途是解决内存不足,而不是降低碎片化,与Windows相反,Linux有时反而会尝试将整个内存占满),另外,依据MSDN资料显示,当用户关闭了虚拟内存机制的时候,WinNT内核会使用临时文件来完成这个操作,这样带来的好处是,这些文件在使用后会被系统及时清理,相较于pagefile更为安全,坏处即为由于内存页的数据以小体积临时文件的形式存在于硬盘之上,受制于硬盘机械结构和文件碎片化,性能会有所下降。
至于广受诟病的内存有时不会自动释放,这又涉及到另外的问题。在操作系统为应用程序分配和释放内存时,操作系统需要执行一系列复杂的操作后才能将内存空间交予应用程序。在WinNT的内存管理机制中,Microsoft使用了二级控制的方式,操作系统会预先分配掉很大数量的内存,这部分内存在外部设备和应用程序看来与未分配的内存没有区别,但是实际上,这些空间已经被系统内核分配并予以保留,当新的应用程序或者已有的应用程序向操作系统申请空间时,这些空间会被优先分配,分配的过程也只不过是在与分配的维护列表中将这部分标记为不可用,并且将这部分内存加入应用程序的内存视图即可。
在WinNT系统中,有一些内存在应用程序生命周期结束时是不会被自动释放的,这部分内存里面存放的不是普通的应用程序数据,而是dll(Dynamic Link Library)文件。dll文件是应用程序库文件,文件内部包含若干函数,这些函数可以被主程序使用。dll与Static Library的区别在于,后者是在编译时被编译进程序之中,这样得到的程序可以单独运行,但是会导致程序体积膨胀、速度下降;而dll文件可以在不增大程序体积、不降低程序性能的前提下为程序提供完整的功能支持,但是动态链接的程序必须依赖dll文件才能运行。为了最大限度的提高应用程序性能,Microsoft针对被注册为shared object&component的dll文件使用的内存管理策略不同于其他程序,这些dll一旦被加载,通常来讲不会被操作系统在其宿主程序生命周期结束后释放,而是会继续被保留一段时间,如果在未来一段时间内没有任何程序调用此文件,操作系统才会将其释放。
时常关注自己的计算机的内存占用的人可能会发现,Windows操作系统的内存占用似乎会随着物理内存总容量的增大而增大,这种现象除了由于WinNT系统刻意控制物理内存使用率尽可能的在某个数值之下以外还涉及到WinNT一个很重要的机制,缓存机制。在整个计算机系统之中,每一个部件之间的速度是不一样的,而计算机系统的性能表现又和每一个部件的速度相关。对于目前绝大多数的计算机系统而言(准确的说,是除了在某实验室中的那一台计算机以外的所有计算机)的性能瓶颈是整套系统的唯一机械结构,温彻斯特硬盘(现在逐渐开始流行的固态硬盘虽然速度较之温彻斯特硬盘比要快不少,但是依然是整套系统的性能瓶颈。)。Microsoft为了尽可能的减小由于硬盘的固有属性导致的不可避免的较低的极限速度,设计了缓冲机制,即当应用程序需要大量读取数据时,操作系统会在内存中开辟一块内存用于预读文件,这样,在应用程序实际需要读文件的时候,操作系统实际将数据从内存中传递,以加快应用程序性能,对于写入操作,操作系统会在内存中开辟一块内存用于保存文件映像,在程序关闭文件时才会将文件写入硬盘。Windows基于这样的操作为应用程序提供了更好的性能支持,同时,不可避免的会带来一些风险,比如突然断电,就有可能造成本来在应用程序看来已经被保存的数据实际上丢失了。同时,由于有大量的业余人员在Windows平台下开发应用程序,这些人对于操作系统的理解并不十分深入,也不会想到要去查阅MSDN或TechNet资料,因此,大量的应用程序开发人员会忽视Windows的缓冲机制,导致自己设计的程序只能适应直接的硬盘读写操作,这样的程序在缓冲机制的作用下反而会出现性能严重降低的情况。
由于Windows内存管理机制的复杂性,Windows的内存占用在用户看来就呈现出了诡异的忽高忽低或者是出现长时间占用不退出的情况,这种情况就催生了一种软件的诞生,即Windows内存清理软件。尽管这些软件的发布者宣称可以清理无用的内存占用,但是,这些软件实际上均为欺骗不明真相的群众。这些软件的实现原理是通过向系统申请超大内存空间,强制系统清空I/O缓存、释放保留的dll、释放系统保留空间、并且大多数内存页压入pagefile。如前所述,这样的后果只会将操作系统性能大幅度降低。