Windows运用程序编写插口(API)是对于Windows电脑操作系统大家族的客户方式系统软件程序编写插口。在32位版本号的Windows营销推广之前,31位版本号Windows电脑操作系统的程序编写插口被称作Win32 API,以差别于原先的18位版本号Windows的程序编写插口,即18位Windows API。在这书中,专业术语Windows API兼指Windows的31位和32位程序编写插口。
深入解析Windows操作系统 下册 PDF英文第6版
注 : Windows软件开发工具(SDK)文本文档叙述了Windows API。那份文本文档能够在WWW.msdn.microsoft.Com免费在线查看。有关怎样在Windows基本API上编写程序,有这份很好的材料---Jeffrey Richter和Christophe Nasarre所作的Windows via C/C++ 一书。
Windows API包括数千个可启用的涵数,他们能够被分为下列某些类别:
这书关键关心重要基础服务项目(例如系统进程和进程,内存管理,I/O和安全系数)的內部原理。
有关.NET Microsoft.NET架构是由1个被称作架构类库(FCL,Framework Class Library)的类库和1个出示了托管代码实行自然环境的公共性語言运行库(CLR,Common Language Runtime)构成的,前者出示的代码执行自然环境包括下列某些特点:
因为CLR具备这种特点,因而它所出示的开发工具可以提升开发者的生产率,降低常用的程序编写不正确。 有关.NET架构以及关键构架的精采叙述,参加Jeffrey Richter的CLR via C#
Win32 API的历史时间 有趣的是,Win32并非Windows Nt最开始预订的程序编写插口。由于Windows Nt新项目在起动之初,目地是替代Os/2第2版,因此,它的关键程序编写插口是31位Os/2 Presentation Manager API。 显然,新项目开展了两年后,Microsoft Windows 5.0 进到销售市场,而且展现出非常好的发展潜力。因此,Microsoft变化了方位,使Windows Nt变成将来Windows商品大家族的代替品,而并不是用于取代Os/2。也更是这一那时候,才真实必须订制Windows API---在此之前,在Windows 5.0 中,只能18位插口的API。 虽然那时候在Windows API上将导入许多在Windows 3.2 上还没法应用的新涵数,可是,Microsoft還是决策让新的API与18位Windows API在涵数名字,词义和数据类型使用方法上尽量适配,便于缓解将现有的的18位Windows手机应用程序移殖到Windows Nt上的承担。这都是很多涵数名字和插口看上去并不是相同的缘故:为了保证那时候新的Windows API与老的18位Windows API维持适配,它是必需的。
在Windows的用户文档和编程设计文本文档中,几个专业术语在不一样的语义自然环境中拥有不一样的含意。比如,服务项目能够指电脑操作系统中能够被启用的例程,机器设备驱动安装或是网络服务器系统进程。下边的目录叙述了某些特殊的专业术语在这书中的含意:
虽然表层上看上去程序流程和系统进程十分类似,但实质上他们确是迥然不同的。程序流程是1个静态数据的命令编码序列,而系统进程是1个器皿,至少包括了程序执行的特殊案例需要的各种各样資源。从最大层级的抽象性看来,Windows系统进程是由下列原素组成的:
每一系统进程也对准它的父系统进程或是创建者系统进程。要是父系统进程不要存有,子系统进程中的这种信息内容并不容易被升级。
有关系统进程和进程的信息内容,Sysinternals荣誉出品的Process Explorer出示了比别的一切专用工具必须多的关键点,正由于这般,你将会在这书的很多试验中见到这一专用工具的应用。
线程是一个进程内部的实体,也是Windows执行此进程时的调度实体。如果没有线程,进程的程序将不可能运行起来。线程包括以下一些最基本的部件:
易失的寄存器,栈和私有存储区域结合起来被称为线程的环境(context)。因为这些信息随着Windows所在机器架构的不同而有所不同,所以,此架构必须是与底层架构相关的。Windows的GetThreadContext函数允许程序访问这一与架构相关的信息(称为CONTEXT块)。
因为将CPU的执行从一个线程切换到另一个线程,将不可避免地涉及内核调度器,所以,这可能是一个开销昂贵的操作,如果两个线程经常频繁来回切换则尤为如此。Windows实现了两种机制来降低这种开销:纤程(fiber)和用户模式调度(UMS)
纤程使得一个应用程序可以调度它自己的"线程"的执行过程,而不必依赖Windows内置的基于优先级的调度机制。纤程也常被称为"轻量"线程:从调度的角度来看,它们对于内核是不可见的,因为它们是在用户模式下在Kernel32.dll中实现的。使用方法参考Windows SDK文档。
UMS线程仅在64位Windows长可用,它基本上提供了与纤程同样的好处,没有更多的坏处。UMS线程有它们自己的内核线程状态,因此对于内核是可见的,这使得多个UMS线程都可以发出阻塞的系统调用,对资源进行共享或竞争,并且有每个线程特有的状态(per-thread state)
虽然线程有自己的执行环境,但是,同一个进程内部的所有线程共享该进程的虚拟地址空间(以及同属于该进程的其他资源),这意味着,一个进程内的所有线程都可以完全地读或写该进程的虚拟地址空间。然而,一个进程中的线程不可能无意识地引用另一个进程的地址空间,除非两种情况:第二个进程将它的一部分私有地址空间变成共享内存区(shared memory section)(在Windows API中称为文件映射对象(file mapping object));或者,第一个进程有权打开第二个进程,从而可以使用诸如ReadProcessMemory 和 WriteProcessMemory 等跨进程的内存函数。
除了私有地址空间和一个或者多个线程以外,每个进程还有一个安全环境和一个已打开句柄的列表,这些句柄指向诸如文件,共享内存区,或者像互斥体,事件或者信号量等某个同步对象
每个进程都有一个安全环境,存储在一个称为访问令牌(access token)的对象中。进程的访问令牌包含了该进程的安全标识和凭证。在默认情况下,线程没有自己的访问令牌,但是他们也可以获得一个访问令牌,因此单独的线程可以模仿另一个进程的安全环境。
虚拟地址描述符(AVD)是指一些数据结构,内存管理器利用这些数据结构来记录进程正在使用的虚拟地址。
Windows在进程模型上提供了一个拓展,称为作业(job)。作业对象的主要功能是,使一组进程被当做一个整体来管理和维护。通过作业对象,可以对特定属性进行控制,也可以对一个进程,或者所有与作业相关联的进程进行限制。作业对象也可以为所有与该作业相关联的进程记录下基本审计信息,其中也包括曾经与改作业关联但是已经终止了的进程的审计信息。在某种程度上,作业对象弥补了Windows平台上缺乏结构化的进程树的不足,而且,它的功能。
Windows实现了一个基于平面(线性)地址空间的虚拟内存系统,使每个进程感觉自己独立拥有一个很大的私有地址空间。虚拟内存提供了一个内存逻辑视图,它可能并不对应于内存的物理布局。在运行的时候,内存管理器借助于硬件的支持,将虚拟地址转译或者映射(map)成真正存放数据的物理地址。操作系统通过控制这一保护和映射机制,可以确保一个进程不会闯入到另一个进程中,也不会改写操作系统的数据。 因为大多数系统所拥有的物理内存,比当前正在运行的进程所用到的虚拟内存总量要少得多,所以,内存管理器会将内存中的有些内容转移或者翻(page)到磁盘上。将数据翻译到磁盘上以后,就可以释放这部分物理内存,因此,这些物理内存可以被别的进程所使用,或者用于操作系统本身。当线程访问一个已被翻到磁盘上的虚拟地址时,虚拟内存管理器会将磁盘上的信息装回内存中。应用程序无须任何改变就可以利用这种分页(paging)功能,因为在硬件的支持下,内存管理器无须任何关于进程和线程的知识,也无须进程或线程的协助,就可以实现分页。 虚拟地址空间的大小随着硬件平台而有所不同。在32位x86系统中,总的虚拟地址空间有一个理论上的最大值4GB。在默认情况下,Windows将这一部分地址空间的一半(4GB虚拟地址空间中较低的一半,x00000000x7FFFFFFF)分配给进程,作为它们独有的私有存储空间,而另一半(地址空间中较高的一半,x80000000xFFFFFFFF)则用于它自己的被保护的操作系统内存。低一半地址空间的映射关系会发生变化,以便总是反应出当前正在执行的进程的虚拟地址空间,而高一半地址空间的映射关系总是由操作系统的虚拟内存构成。Windows支持一些引导选项,可以使那些运行带有特殊标记的程序(在可执行映像文件头部设置了大地址空间感知标志)的进程能够使用多达3GB的私有空间(给操作系统留下1GB)。这一选项使得像数据库服务器这样的应用程序的更多内容保留在进程的地址空间中,从而减少映射该数据库的子集视图的需求。 虽然3GB比2GB更好,但是,对于映射非常大的数据库(许多个GB)而言,虚拟地址空间任然不足。针对在32位系统上的这种需求,Windows提供了一个被称为地址窗口扩展(AWE,Addres Windowing Extension)的机制,使得32位应用程序可以申请多达64GB物理内存,然后将内存视图或者窗口映射到它的2GB虚拟地址空间中。虽然AWE将管理"虚拟内存-物理内存"映射关系的负担放到了程序员的身上,但是,它确实解决了问题,使得进程能够直接访问更多的物理内存。超过了它的32位进程地址空间一次能够映射的数量。 64位Windows为进程提供了更大的地址空间:在IA-64系统上位7152GB,x64系统上为8192GB。注意,这些大小值并不代表这些平台上的架构限制。64位地址空间的大小超过了170亿GB,但当前的64位硬件限制了地址空间比该值要小,而在当前的64位Windows中,由于Windows的实现机制,此地址空间又被降低到8192GB(8TB)。
为了避免用户应用程序访问和/或修改关键的操作系统数据,Windows使用了两种处理器访问模式(即使运行Windows的底层处理器支持多余两种模式):用户模式和内核模式。用户程序运行在用户模式下,而操作系统代码(比如系统服务和设备驱动程序)运行在内核模式下。内核模式是指这样一种处理器执行模式:它允许访问所有的系统内存和所有的CPU指令。通过让操作系统软件比应用软件有更高的特权级,处理器位操作系统设计者提供了必要的保护,可以确保行为不正常的应用程序不会破环系统整体的稳定性。
注: x86和x64处理器架构定义了四种特权级,或者称为四个环(ring),来保护系统代码和数据不会被低级别的代码恶意地或无意地改写。Windows使用特权级0(或称为0环)作为内核模式,特权级3(或称3环)作为用户模式。Windows之所以只使用两级,是因为它过去支持的一些硬件架构(比如Compaq Alpha和Silicon Graphics MIPS)只实现了两个特权级。
虽然每个Windows进程都有自己私有的内存空间,但是内核模式的操作系统和设备驱动程序代码共享一个虚拟地址空间。虚拟内存中的内一个页面都被标记了处理器必须在什么访问模式下才可以读和/或写该页面。系统空间中的页面只有在内核模式下才可以访问,而用户地址空间的所有页面都可以在用户模式下访问。只读页面(比如包含静态数据的页面)在任何模式下都是不可写的。此外,在支持不可执行(no-execute)内存保护的处理器上,Windows将包含数据的页面标记为不可执行,从而防止数据区域被无意或恶意地当作代码来执行。
对于在内核模式下运行的组件,32位Windows对它们所使用的私有系统内存并不提供读写保护。换句话说,一旦进入内核模式,操作系统和设备驱动程序的代码可以完全访问系统空间的内存,也可以绕过Windows的安全机制直接访问对象。因为有大量的Windows操作系统代码运行在内核模式下,所以很关键的一点是,对在内核模式下运行的组件必须要谨慎地设计和测试,以确保它们不会破坏系统的安全性,也不会造成系统不稳定。
由于缺乏保护,因此,再加载第三方设备驱动程序时更需要加倍小心,因为一旦进入内核模式,这些软件就可以完全访问所有的操作系统数据。这一弱点也正是Windows要引入驱动程序签名机制的原因之一。引入驱动程序签名机制以后,当未签名的即插即用驱动程序企图加入系统中时,Windows会警告用户,并且阻止其加入系统(如果做了这样的配置)。还有一种被称为驱动程序检验器(Driver Verifier)的机制,可以帮助设备驱动程序的编写者找到驱动程序中引发安全性或可靠性问题的缺陷(比如缓存区溢出或者内存泄漏)。
在64位版本的Windows中,内核模式代码签名(KMCS,Kernel Mode Code Signing)策略规定,64位设备驱动程序(不仅仅是即插即用程序)必须要经过某个主码认证权威机构发放的密钥来签名。用户不能明确地强制安装未经签名的驱动程序,即使管理员也不行,但有一个一次性的例外,这一限制可以在系统引导时手工禁止,即,在引导时按下F8键,选择高级引导选项"禁用驱动程序强制签名(Disable Driver Signature Enforcement)"。这会导致在桌面壁纸上出现一个水印,同时特定的数字版权保护(DRM)特性被关闭。
在第2章"系统架构"中将看到,用户应用程序将在进行系统服务调用时,会从用户模式切换到内核模式下。例如,WIndows的ReadFile函数最终要调用Windows内部的一个例程,由该例程真正完成从一个文件中读取数据的任务。由于该例程需要访问内部的系统数据结构,所以,它必须运行在内核模式下。从用户模式切换到内核模式,可以通过专门的处理器指令来完成,该指令会将处理器切换到内核模式下,并进入内核模式中的系统服务分发代码,进一步调用Ntostrnl.exe或Win32k.sys中适当的内部函数。在将控制权返回用户线程之前,处理器的模式被切换回用户模式。通过这种方法,操作系统将自身和它的数据保护起来,使它们不会被用户进程看到或者修改。 因此,很自然地,对于一个用户线程来说,它的一部分时间运行在用户模式下,另一部时间运行在内核模式下。实际上,因为图形和窗口系统的大部分时间也运行在内核模式下,所以,图形密集的应用程序花在内模式下的时间比在用户模式下的时间多得多。这一点很容易测试,一种简单的办法是,运行一个图形密集的应用程序,比如"画图"或者Chess Tians,然后使用性能监视器(具体方法百度)
终端服务指的是在单个系统中,Windows对于多个可交互用户会话的支持。利用Windows的终端服务,一个远程用户可以在另一台机器上建立一个会话,并且登陆进去,在该服务器上运行应用程序。服务器把图形用户界面(以及其他可配置的资源,比如音频和剪切板)传送到客户机,客户机把用户的输入传回服务器。(与X窗口系统类似,Windows允许在一个服务器系统运行独立的应用程序,其显示部分远程传送到客户机,而非将整个桌面远程到客户机)。 第一个会话被认为是服务会话,或者零号会话,它包含了宿纳系统服务的进程。在机器的物理控制台上的第一个登陆会话位一号会话,而其他会话可以通过远程桌面连接程序(Mstsc.exe)来建立,或者通过使用快速用户切换来建立。
Windows的客户机版本允许单个远程用户连接到及其上,但如果有人已经在控制台上登陆了,则工作站会被锁住(也就是说,一个人可以利用本地或者远程方式使用Windows客户机系统,但不能同时以这两种方式来使用系统)。
Windows服务器系统支持两个并发的远程连接(这是为了方便远程管理,例如,有些管理工具要求登录到被管理的机器中才可以使用),以及两个以上的远程会话。
所有Windows客户机版本都支持通过使用一个被称为快速用户切换(fast user switching)的特性,在本地创建多个会话。当用户选择断开其会话,而不是注销其登录时,当前会话仍然保留在系统中,而系统返回到主屏幕。如果一个新的用户登陆进来,则新建一个会话。
在Windows操作系统中,内核对象是指某个静态定义的对象类型的单个运行时实例。对象类型由系统定义的的数据类型,在该数据类型的实例上进行操作的一组函数,以及一组对象属性构成。如果你编写Windows应用程序,那么,你可能会遇到进程,线程,文件和事件对象,这只是对象的一些例子而已。这些对象都是以Windows创建和管理的底层对象为基础的。在Windows中,任何进程都是进程对象类型的实例,文件是文件对象类型的实例,如此等等。
对象属性是对象中的数据域,每个对象属性定义了对象的一部分状态。例如,类型为进程的对象,其属性包括进程ID,基本调度优先级和一个指向访问令牌对象的指针。对象方法,即操纵对象的手段,通常用于读取或者改变对象的属性。例如,进程的open方法接受一个进程标识符作为输入,返回一个指向该进程对象的指针作为输出。
对象和普通数据结构之间的最根本区别是,对象的内部结构是不透明的。你必须调用一个对象服务才可以获得对象内部的数据,或者把数据放入对象内部。你不能直接读取或者改变一个对象内部的数据。这一区别将对象的底层实现与那些仅仅使用该对象的代码隔离开来,此技术使得对象的实现可以随着时间而容易地改变。
借助一个被称为对象管理器的内核组件,Windows中的对象提供一种便捷的途径来实现下列四个重要的操作系统任务:
并非Windows操作系统中的所有数据结构都是对象。只有确实需要被共享,保护,命名或者让用户模式程序看得到(通过系统服务)的数据才被放入到对象中。仅仅被操作系统的某一个组件用来实现其内部函数的数据结构并不是对象。
Windows从一开始就被要求是安全的,能够满足*和工业界各种正式的安全评级的需求
Windows的核心安全功能包括:针对所有可共享系统对象(比如文件,目录,进程,线程等等)的自主保护(need-to-know)和强制完整性保护,安全审计(针对主体或者用户和他们发起的动作的记录),登陆时候的用户认证,以及禁止用户通过访问未初始化资源的做法来访问其他用户已释放的资源(比如空闲内存或磁盘空间)。
Windows有三种对对象的访问控制形式。第一种控制形式称为自主访问控制,大多数人一想到操作系统安全性,自然就会想到这种保护机制。其做法是,由对象(比如文件或者打印机)的所有者授权或者拒绝其他人访问这些对象。当用户登陆系统中时,他们会得到一组安全凭证,或者一个安全环境。当他们试图访问对象时,系统会将他们的安全环境与他们要访问的对象的访问控制列表进行比较,以确定他们是否可以执行所请求的操作。
当自主访问控制不能满足需要时,应该使用特权访问控制。这种方法可以确保:即使对象的所有者当前无法联系到,也有人可以访问被保护的对象。例如,如果一个员工离开了公司,则管理员必须有办法访问那些本该只有该员工才可以访问的文件。在这种情况下,在Windows中,管理员能够接管这些文件的所有权,从而可以根据需要管理它们的权限。
最后,当需要额外的一层安全控制以实现在同一个账户内部的对象访问保护时,需要使用强制完整性控制。它既被用于将Internet Explorer的保护模式与用户的配置隔离开,也被用于保护那些由提升了特权的管理员所创建的对象,避免被未被提升特权的管理员账户所访问。
如果你与Windows操作系统打过交道,那么,你可能听说过或者看到过注册表。探讨Windows内部机理,就不能不提到注册表,因为注册表是系统数据库,它包含了引导和配置系统所必需的消息,全系统范围内控制Windows操作的软件设置,安全数据库,以及针对每个用户配置设置(比如使用哪个屏幕保护程序)。
同时,注册表也是一个反映内存中易失数据的窗口,比如系统的当前硬件状态(哪些设备驱动程序已经加载,它们用到了哪些资源,等等)以及Windows的性能计数器。
虽然许多Windows用户和管理员从来不需要直接查看注册表(因为你可以通过标准的管理工具来查看或者改变大多数配置设置),但是,注册表仍然是一个非常有用的Windows内部信息来源,因为它包含了许多会影响系统性能和行为的设置。在本书中你将会看到许多注册表键,当介绍某些组件时,将会提及相应的注册表键。本书中提到的绝大多数注册表键位于全系统范围的配置(即HKEY_LOCAL_MACHINE)下面,我们将其缩写位HKLM。
Windows区别于大多数其他操作系统的一个方面是,它的大多数内部文本串是以16位宽度的Unicode字符来存储和处理的。
因为许多应用程序只处理8位(单字节)ANS字符串,所以,接受字符串参数的Windows函数有两个入口点:一个Unicode(宽字符,16位)版本和一个ANSI(窄字符,8位)版本。