1、Windows API
Windows应用程序编程接口(API):针对Microsoft Windows操作系统家族的系统编程接口。每个操作系统实现了Windows API的不同子集。Windows API包括几千个可调用的函数,可分为7类:
①基本服务
②组件服务
③用户界面服务
④图形和多媒体服务
⑤消息和协作
⑥网络
⑦Web服务
2、SDK(Platform Software Development Kit,平台软件开发工具)
SDK文档描述了Windows API。可通过msdn.miscrosoft.com查看SDK文档,包含所有订阅级的MSDN(Miscrosoft Developer Network),指的是Miscrosoft对开发人员的支持计划。
3、.NET和WinFX
.NET框架是由一个被称为框架类库(FCL,Framework Class Library)的类库和一个提供了托管代码执行环境的公共语言运行库(CLR,Common Language Runtime)组成的,提供的托管代码执行环境包含的特性:即时编译、类型检查、垃圾回收和代码访问安全性等。因CLR的这些特性,它提供的开发环境能够提高开发人员的生产效率,减少常见的编程错误。
CLR的具体实现形式是一个典型的COM服务器,它的代码位于一个用户模式的标准Windows DLL中。实际上,.NET框架中所有组件的实现形式都是用户模式的标准Windows DLL。建立在非托管的Windows
API之上(.NET框架中没有一个组件运行在内核模式下)。
WinFX是"新的Windows API",是.NET框架演进版本,为建立在Windows操作系统之上的下一代应用程序提供了一个基础平台。
4、Win32 API
32位版本的Windows操作系统的编程接口。
5、服务、函数和例程
“服务”可以指操作系统中一个可被调用的例程、一个设备驱动程序或者一个服务器进程。
Windows API函数
Windows API中已被文档化的可调用子例程,如CreateProcess、CreateFile和GetMessage等
原生的系统服务或执行体系统服务
操作系统中未文档化的、可在用户模式下调用的底层服务。如NTCreateProcess等
内核支持函数或例程
位于Windows操作系统内部且只能在内核模式下调用的子例程,如ExAllocatePool等
Windows服务
由Windows服务控制管理器启动的进程。如"任务调度器"服务运行在一个用户模式的进程中,支持at命令
DLL动态链接库
一组可调用的子例程,合起来被链接成一个二进制文件,使用这些子例程的应用程序可以动态地加载此二进制文件。相比静态库的优势:应用程序可以共享DLL,Windows保证在内存中只有一份DLL代码,供所有引用该DLL的应用程序共享。
6、进程、线程和作业
进程
程序是指一个静态的指令序列,而进程则是一个容器,其中包含了当执行一个程序的特定实例时所用到的各种资源。从最高层次的抽象看,一个Windows进程是由以下元素构成的:
①一个私有的虚拟地址空间,指该进程可以使用的一组虚拟内存地址
②一个可执行的程序,定义了初始的代码和数据,并且被映射到该进程的虚拟地址空间中
③一个已打开句柄的列表,这些句柄指向各种系统资源,比如信号量、通信端口和文件,该进程内所有的线程都可以访问这些系统资源
④一个被称为访问令牌的安全环境,标识了与该进程关联的用户、安全组和特权
⑤一个被称为进程ID的唯一标识符(在内部被称为客户ID)
⑥至少一个执行线程
每个进程也指向它的父进程挥着创建者进程。若父进程退出,子进程中的父进程信息并不会被更新,因此一个进程有可能指向一个已经不再存在的父进程。任何一个进程都不必依赖于父进程信息的有效性。
大多数工具在显示一个进程的属性时,通常不会显示该进程的父进程或者创建者进程的ID。使用性能工具查询Creating Process ID得到父进程ID。Tlist.exe工具的/t开关打开后可以显示进程树。Windows仅仅维护创建者进程的ID,无法再链回到创建者进程的创建者进程。
最经常用来检查进程行为状态的工具可能是任务管理器(在Windows的内核中并不存在"任务"概念),实际是管理进程的工具。
除了私有的地址空间和一个或者多个线程以外,每个进程还有一个安全标识和一个已打开句柄的列表,这些句柄指向诸如文件、共享内存区,或者互斥体、事件或信号量等同步对象等。
每个进程都有一个安全环境,被存储在一个称为访问令牌的对象中。进程的访问令牌包含了该进程的安全标识和凭证。在默认情况下,线程没有自己的访问令牌,但它们也可以包含一个访问令牌,因此单独的线程可以模仿另一个进程的安全环境——包括在远程Windows系统上运行的进程而不影响到当前进程中的其他线程。
线程
是一个进程内部的实体,也是Windows执行此进程时的调度实体。没有线程,进程的程序不可能运行。线程包括以下基本部件:
①一组代表处理器的CPU寄存器中的内容
②两个栈,一个用于当线程在内核模式下执行的时候;一个用于线程在用户模式下执行的时候
③一个被称为线程局部存储区(TLS,thread-local storage)的私有存储区域,各个子系统、运行库和DLL都会用到该存储区域
④一个被称为线程ID(threadID,线程标识符)的唯一标识符(在内部被称为客户ID——进程ID和线程ID是在同一个名字空间中生成的,所以它们永远不会重叠)
⑤有时候线程也有它们的安全环境。若多线程服务器应用程序要模仿其客户的安全环境,可利用线程的安全环境。
易失的寄存器、栈和私有存储区合起来被称为线程的环境。这些信息随着Windows所在的机器的体系结构的不同而有所不同,所以这些数据结构必须是与底层体系结构相关的。通过Windows提供的GetThreadContext函数,可访问到与这一体系结构相关的信息(称为CONTEXT块)。
纤程(fiber)与线程
纤程使得一个应用程序可以调度它自己的“线程”的执行过程,而不必依赖于Windows内置的基于优先级的调度机制。纤程也被称为“轻量级”的线程。从调度的角度看,它们对于内核是不可见的,因为它们是在用户模式下实现的,位于Kernel32.dll中。为了使用纤程,首先要调用Windows的ConvertThreadToFiber函数,该函数将当前纤程转变成一个正在运行的纤程。之后在转变得到的纤程中,通过调用CreateFiber函数,又可以创建新的纤程(每个纤程可以有它自己的一组纤程)。与线程不同的是:纤程不会自动被执行,必须手动调用SwitchToFiber函数选中一个纤程,才能使它运行。新的纤程一直运行,直到它退出,或者直到它调用SwitchToFiber,再次选择运行另一个纤程。
线程有自己的执行环境,但是同一个进程内部的所有线程共享该进程的虚拟地址空间以及其余的属于该进程的资源,意味着一个进程内的所有线程可以读或者写另一个线程的内存。但是一个进程中的线程不可能直接引用另一个进程的地址空间,除非两种情况:①第二个进程将它的一部分私有地址空间变成共享内存区(在Windows API中称为文件映射对象);②第一个进程有权打开第二个进程,从而可以使用诸如ReadProcessMemory和WriteProcessMemory等跨进程的内存函数。
虚拟地址描述符
VAD(virtual address descriptor)是指一些数据结构,内存管理器利用这些数据结构来记录一个进程所使用的虚拟地址。
作业
Windows在进程模型上作了一个扩展,称为作业。作业对象的主要功能是:使得一组进程被当做一个整体来管理和维护。作业对象允许对特定的属性进行控制,也允许对一个进程,或者所有与作业相关联的进程进行限制。作业对象也为所有与该作业相关联的进程记录下基本的审计信息,其中包括曾经与该作业关联过但是已经终止了的进程的信息。在某些方面,作业对象不仅弥补了Windows平台上缺乏结构化的进程树的不足,功能也比UNIX风格的进程树更为强大。
7、虚拟内存
Windows实现了一个基于平面(线性)地址空间的虚拟内存系统,每个进程独立拥有一个很大的私有地址空间。虚拟内存提供一个内部逻辑视图,它可能并不对应于内存的物理布局。在运行时,内存管理器借助于硬件的支持,将虚拟地址映射成真正存放数据的物理地址。操作系统通过控制这一层保护或者映射机制,可以保证一个进程不会闯入到另一个进程中,也不会改写操作系统的数据。
系统所拥有的物理内存是小于虚拟内存大小的,因此内存管理器会将内存中的内容转移(翻)到虚拟内存中,接着释放物理内存。当线程访问一个已被转移到虚拟内存中的内容时,虚拟内存管理器会将这一部分的内容放回物理内存中。在硬件的支持下,应用程序不需要任何的改变,也不需要进程或线程的协助就可以实现这种翻页。
虚拟地址空间的大小随着硬件平台的不同而有所差异。在32位X86系统中,总的虚拟地址空间有一个理论的最大值4GB。默认情况下,Windows将这部分地址空间的一半(低地址0x00000000——0x7fffffff)分配给进程,作为它们独有的私有存储区,另一半(高地址0x80000000——0xffffffff)用作它自身的受保护的操作系统内存。低一半地址空间的映射关系反映了当前执行进程的虚拟地址空间,高一半地址空间的映射关系总是由操作系统的虚拟内存构成的。有一些Windows系统带有特殊标记的程序(在可执行文件的头部设置大地址空间感知标志)启动的进程可以使用高达3GB的私有地址空间,如此像数据库服务器之类的应用程序可以将更多的数据库内容映射到进程的地址空间中,从而减少映射数据库子集视图的需求。
对于映射非常大的数据库,虚拟地址空间仍然不足。针对在32位系统的需求下,Windows提供了一种被称为地址窗口扩展(AWE,Address Windowing Extension)的机制,使得32位应用程序可以申请多达64GB物理内存,接着讲内存视图或者窗口映射到它的2GB虚拟地址空间中。64位Windows为它的进程提供了更大的地址空间:在Itanium系统上为7152GB,在x64系统上为8192GB。
8、内核模式和用户模式
为了避免用户程序访问/修改关键的操作系统数据,Windows使用了两种处理器访问模式:用户模式和内核模式。用户程序代码运行在用户模式下,而操作系统代码运行在内核模式下。内核模式是指:它允许访问所有的系统内存和所有的CPU指令。处理器让操作系统比应用软件有更高的特权级,从而为操作系统设计者提供了一层保护的基础,确保一个行为不正常的应用程序不会破坏系统整体的稳定性。Intel x86处理器的体系结构定义了四种特权级,来保护系统代码和数据不会被低级别的代码恶意地或无意地改写。Windows使用特权级0作为内核模式,特权级3作为用户模式。Windows只用两级的原因是:过去支持的有些硬件体系结构只实现了两个特权级,如Compaq Alpha和Silicon Graphics MIPS。
虽然每个Windows进程有它自己私有的内存空间,但内核模式的操作系统和设备驱动程序共享同一个虚拟地址空间。虚拟内存中的每一个页面都被标记了处理器必须在什么访问模式下才可以读/写该页面。系统空间中的页面只能在内核模式下才可以访问,而用户地址空间中的所有页面在用户模式下都是可以访问的。只读页面在任何模式下都不是可写的。
对于在内核模式下运行的组件,Windows对它们的读写系统内存并不加以保护。就是说一旦进入内核模式,操作系统和设备驱动程序的代码可以完全访问系统空间的内存,也可以绕过Windows的安全机制直接访问对象。有大量的Windows操作系统代码运行在内核模式下,所以对在内核模式下运行的组件必须要谨慎设计和测试,以确保它们不会破坏系统的安全性,这一点很重要。
Windwos之所以要引入驱动程序签名的原因也是因为一旦进入内核模式,软件就可以完全访问所有的操作系统数据。引入驱动程序签名机制后,当一个没有授权(未签名)的驱动程序企图加入到系统中时,Windows会警告用户。被称为驱动程序检验器(Driver Verifier)的机制可以帮助设备驱动程序的编写者找到程序中的错误,如内存泄漏或缓冲区溢出等。
用户应用程序在发出一个系统服务调用时,就会从用户模式切换到内核模式下。从用户模式切换到内核模式,可通过专门的处理器指令来完成,这条指令会将处理器切换到内核模式下。操作系统捕捉到这条指令,注意到有一个系统服务请求到来,它检查用户线程传递给系统函数的所有实参的有效性,接着执行相应的内部函数。在将控制返回给用户线程以前,处理器的模式被切换回到用户模式。通过这种方法,操作系统将自身保护起来,同时也保护了它的数据不会被用户进程看到或修改。从用户模式切换到内核模式,本身并不会影响线程的调度(模式转换并不是环境切换)。
因此对于一个纤程而言,它的一部分时间在用户模式下运行,另一部分时间在内核模式下运行,这是很正常的。实际上因为大部分图形和窗口系统也运行在内核模式下,所以图形密集的应用程序花在内核模式下的时间比在用户模式下的时间还要多。
对象:计数器 | 功能 |
---|---|
Processor:% Privileged Time | 在指定的间隔内,单个CPU(或者所有CPU)运行在内核模式下的时间所占的百分比 |
Processor:% User Time | 在指定的间隔内,单个CPU(或者所有CPU)运行在用户模式下的时间所占的百分比 |
Process:% Privileged Time | 在指定的间隔内,一个进程中的线程运行在内核模式下的时间所占的百分比 |
Process:% User Time | 在指定的间隔内,一个进程中的线程运行在用户模式下的时间所占的百分比 |
Thread:% Privileged Time | 在指定的间隔内,一个纤程运行在内核模式下的时间所占的百分比 |
Thread:%User Time | 在指定的间隔内,一个纤程运行在用户模式下的时间所占的百分比 |
可利用QuickSlice查看线程活动情况,它动态显示当前系统中正在运行的每个进程所使用的系统和内核时间的比例。红色部分表示该进程在内核模式下所花CPU的时间,蓝色部分显示用户模式下所花的时间。
9、终端服务及多个会话
终端服务是指Windows为了在单个系统中支持多个可交互的用户会话而提供的能力。利用Windows的终端服务,一个远程用户可以在另一台机器上建立一个会话,并且登录进去,在该服务器上运行应用程序。服务器把图形用户界面传送到客户机,客户机把用户的输入传回到服务器上。
在机器的物理控制台上的第一个登录会话被认为是控制台会话,或者零号会话。其他的会话可以通过远程桌面连接程序Mstsc.exe来建立,或者在Windows XP系统中,通过使用快速用户切换建立。若应用程序需要确定是否运行在一个终端服务器会话中,可通过Windwos提供的一组API检测,同时允许它们控制终端服务的各个方面。
10、对象和句柄
在Windows操作系统中,对象是指某一个静态定义的对象类型的单个运行时的实例。对象类型包含了一个系统定义的数据类型、在该数据类型的实例上进行操作的一些函数,以及一组对象属性。对象属性是对象中的数据域,每个对象属性定义了对象的一部分状态。如类型为进程的对象,其属性包括进程ID、一个基本调度优先级和一个指向访问令牌对象的指针。对象方法即操纵对象的手段,通常读取或者改变对象的属性。
对象和普通数据结构之间最根本的区别是:对象的内部结构是隐藏的。必须调用一个对象服务才可以获得对象内部的数据,或者把数据置到对象内部。不可以直接读取或改变一个对象内部的数据。这一区别将对象的底层实现和使用该对象的代码隔离。对象技术提供了一种便携的途径实现下列4个重要的操作系统任务:
①为系统资源提供可供人读的名字
②在进程之间共享资源和数据
③保护资源,避免未授权的访问
④引用跟踪,使系统得到对象何时不再使用,被自动释放
并不是Windwos操作系统中的所有数据结构都是对象。只有确实需要被共享、保护、命名或者让用户模式的程序看到(通过系统服务)的数据才被放到对象中。仅仅被操作系统的某个组件用来实现其内部函数的数据结构并不是对象。
11、安全性
Windows的核心安全功能包括:针对所有可共享对象(比如文件、目录、进程、线程等)的自主保护(need-to-know),安全审计(针对主体或者用户和它们发起的动作的记录),登录时的口令认证,以及在一个用户释放了某一资源以后,另一个用户无法通过访问未初始化资源看到前一个用户留下的资源。
针对系统内部的对象,Windows有两种访问控制形式。第一种控制形式称为自主访问控制。其做法是:由对象的所有者授权或者拒绝其他人访问这些对象。当用户登录到系统中时,会得到一组安全凭证,或者一个安全环境。当他们试图访问对象时,系统会将它们的安全环境与它们要访问的对象上的访问控制列表进行比较,以确定它们是否允许执行所请求的操作。
第二种控制形式称为特权访问控制。这种访问控制方法可以确保:即使无法联系到对象的所有者,某些人也能够访问被保护的对象。
安全性遍及Windows API接口的各个方面。Windows子系统实现了基于对象的安全性,其方式与操作系统的做法相同:Windows子系统保护共享的Windows对象,在这些对象上设置了Windows安全描述符,从而可以避免它们被非法访问。如果一个应用程序第一次试图访问一个共享对象,Windows子系统会验证该应用程序是否有权限,检查成功就允许应用程序继续执行。
Windwos子系统在许多共享对象上实现了对象安全性,其中有些共享对象建立在原生的Windows对象之上。Windwos对象包括:桌面对象、窗口对象、菜单对象、文件、进程、线程和一些同步对象。
12、注册表
注册表可以说是系统数据库,它包含了引导和配置系统所必要的信息、系统范围的控制Windwos操作的软件设置、安全数据库,以及针对每个用户的配置信息。同时注册表是一个反映内存中易失数据的窗口,比如系统中当前硬件的状态以及Windows的性能计数器。性能计数器实际上并不“位于”注册表中,但可通过注册表函数访问。
13、Unicode
Windows操作系统区别于大多数其他操作系统的特点之一是:它的大多数内部文本串是以16位宽度的Unicode字符来存储和处理的。Unicode(统一的字符编码标准)是一个国际字符集标准,它为世界上绝大多数已知的字符集定义了唯一的16位值。许多应用程序只处理8位(单字节)ANSI字符串,所以接受字符串参数的Windows函数都有两个入口点:一个Unicode(宽字符,16位)和一个ANSI(窄字符,8位)版本。有些Windows版本并没有为所有的Windows函数实现Unicode接口,所以若设计的应用程序要在这些操作系统上运行,需要使用窄字符版本。
调用一个Windows函数的窄字符版本,输入的字符串参数在被系统处理之前先转成Unicode,输出的参数则在被返回给应用程序之前,从Unicode转成ANSI字符串。Windows永远不会转换文件内部的数据,由应用程序来决定是否要存储为Unicode或者ANSI。现在的Windows版本都包含同样的Windows函数,同一份安装可以支持多种语言,只需要加入各种语言包即可。