由系统中每一个进程产生的io操作。针对每个进程存在两条折线。其中的一条用于记录数据操作,另一条用于记录像文件打开和关闭之类的其他操作。很快,系统像我们想象的那样平静下来。现在,让我们回到命令行状态并启动一些将导致许多操作的活动。让我们来看看一个涉及硬盘上全部目录的操作——dir c:/s。这将意味着从c盘根目录开始产生一个自上而下遍历c盘上所有子目录的目录列表。像我们看到的这样,为了显示所需数据正在执行许多目录读取操作。在该操作执行过程中,让我们回到性能监视器,并将其显示内容顶部的折线看作是有某人正在执行大量io操作。好了,现在请考虑如何确定这条代表大量io操作的折线实际所映射的进程。
让我们打开加亮显示功能。单击加亮按钮或使用快捷键ctrl-h 。如果你以前曾经使用过性能监视器,那你一定知道打开加亮功能后,当通过键盘上的下箭头滚动列表中的计数器时,当前在列表框中选中的进程或折线将被加亮显示为白色。因此,我将向下滚动计数器直至当前在顶部的折线变为白色。就是这样。你们能够看到底部显示的实例名称吗?正是cmd。因此,进程cmd便是产生所有这些io操作的那个进程。这很容易理解,因为cmd就在这儿。它就是命令行窗口并且正是它产生了涉及目录的io操作。你可以看到这有多快,使用性能监视器,监视每一个进程每秒内执行的io操作并且快速定位某个io操作由哪个进程负责。好了,这很有趣,但也产生了一个问题,即io操作的去向是哪里?刚刚介绍的内容仅告诉了我们io操作的发生。它将把我们带入幻灯片上的下一个工具——文件监视器。
文件监视器(file mon)是迄今为止我们使用的第三个来源于sysinternals.com网站的工具。像我们前面用过的工具一样,它涉及到一个设备驱动程序的使用。当我们运行文件监视器时,它所做的工作是加载一个可以截取系统中每个io操作的文件系统驱动程序,将每个io操作显示在屏幕上,之后再将其送往相应的设备驱动程序。因此它在很大程度上降低了io操作的速度,但是它确实提供了一种有效的方式用以确定io操作的来源,因为每个io操作都将被它记录,所记录的信息包括进程名称、所引用的文件名称以及操作的类型——读或些。下面让我们来具体地看一看它。
回到命令行状态,从演示目录下通过filemon命令运行文件监视器。现在文件监视器启动了对文件的监视活动。尽管看上去好像有一些io操作正在后台运行,系统还是马上平静下来,事实上,在这个特殊的windows 2000系统中存在一个系统进程,它每隔1或2秒执行一些访问数据库的io操作。现在,让我们回到命令行状态并试着模仿一些针对某个特定文件的繁重的io活动。这个特定目标文件是每个windows 2000系统都拥有的一个大文件,即驱动程序压缩包文件。
这个驱动程序压缩包文件包含随windows 2000一同发布的所有设备驱动程序的压缩版本。它存放在win nt driver cache文件夹中。我将启动window nt资源管理器并进入到我的win nt driver acche文件夹中对这个文件进行一个简单的拷贝操作。我打开c驱动器,展开winnt目录,接着展开driver cache目录以及其中的i386目录,这里存放着driver.cab文件。请注意它的大小是51兆。因此它将是一个理想的测试对象。我将仅仅通过编辑菜单中的拷贝命令和粘贴命令进行一个简单的文件拷贝。你可以看到我们现在正在拷贝一个51兆的文件。
让我们回到文件监视器并观察一下它的显示内容。好的,像我们在输出区域中看到的那样,我们可以观察到大量对driver.cab文件及其备份的io操作。我使用快捷键ctrl-e以终止文件监视器的监视活动,同时我们可以看到一些读写操作——从driver.cab文件进行的读操作以及对其同名拷贝进行的写操作。现在让我们返回并取消文件拷贝,你将看到一个使用文件监视器观察独立的io操作并且使你能够查看io操作发生在哪个文件中的例子。
你可以用文件监视器完成一些过滤工作。例如,如果你只对c或d驱动器或是这些驱动器上一个特殊目录中的io操作感兴趣,你可以对其特定的路径进行过滤,你也可以加亮显示一个指定的路径名以便使你能够轻松地找到所有被监视的文件中你所感兴趣的特殊文件。由此再次验证了,对于观察io活动以及跟踪io操作发生在哪个文件上,文件监视器是一个重要的工具。
这一部分的最后一点是文件监视器可以通过在一行中加入一个小星号来指出分页io操作。由于windows 2000高速缓存管理器使用通常的分页机制和内存管理器来完成文件io操作,你可以看到发生在由应用程序打开的文件中的分页活动。换言之,由于高速缓存子系统通过通常的分页机制使用内存管理器从文件中读取数据,对于一个打开和读取文件的应用程序,其io操作自身在文件监视器中显示为分页读操作。
文件或系统进程活动的最后一个领域,也是同样重要以至要求能够监视的内容是注册活动。如你们中大多数人知道的那样,注册表是nt用于对其自身进行配置的数据库,需要装载的驱动程序、所有的管理设置以及每个用户档案的设置均存放在这里。有时,对于一个系统管理员来说,知道某个特定设置被存储在注册表中的什么位置是很有帮助的。在sysinternals.com网站上有一个称为注册表监视器的工具可以用来监视对注册表的每一次读和(或)写。让我们来运行注册表监视器。我将回到命令行状态并从演示目录中运行从sysinternals.com网站得到的注册表监视器的一个拷贝。当注册表监视器启动后,它将装载一个驱动程序以便开始截取所有对注册表的查询。此刻,作为性能监视器容器的mmc进程也正在进行一些常规的注册表查询。因此,让我们返回并关闭性能监视器以使这些查询工作停止。好,现在系统平静下来了。注册表通常是平静的。换言之,如果一个进程正在执行常规的注册表读或写操作,此时出现了错误,你可能会考虑为厂商整理一份错误报告。在进程或nt启动时,注册表将会被查询。它并不是个会被经常访问的数据库。使用注册表监视器可以在寻找特定系统设置在注册表中存储位置时最大限度的得到启发。例如,如果你启动注册表监视器后进入控制面板,访问一些设置程序或选项卡,你将能够看到在控制面板中所涉及的设置信息在注册表中所处位置的精确跟踪信息,并且它可能引导对注册表的一些更深的研究,从而作为windows 2000资源工具包帮助文件(该文件记录了绝大多数注册表键)的辅助。这便是注册表监视器实用工具。接下来提两个问题。
nt中运行什么?调度的单元是什么?答案是线程。请记住,进程并不运行,线程才能运行。每个进程至少包含一个线程。
线程如何表现得看起来运行了许多,但却并未占用cpu时间呢?一个线程拥有大量与上下文无关的内容,它们使线程可被nt选中运行,但却很少占用或根本不占用cpu时间。答案是:nt使用一个基于间隔的时钟定时器机制来计算cpu时间。如果时钟激发时曾经处于运行状态的线程已不再运行,它将不占用时钟周期。作为缺省时钟间隔,每10毫秒——尽管不同系统的缺省值不同,无论哪个线程是当前线程,它都将被认为占用了这10毫秒的周期。如果没有线程在运行,将被计为被空闲线程占用。空闲线程是任务管理器中所显示的系统空闲进程的一部分。让我们回忆一下,在进程选项卡列表中所列出的第一个进程便是空闲进程。这个进程的作用是:在没有线程运行时,累计并占用所有cpu时钟周期。
最后一个问题。进程地址空间的大小是多少?nt是一个32位操作系统,32位对应于4gb。缺省情况下,nt将地址空间的一半分给用户进程,并将4gb的另一半留给自己使用。
我们已经花费了一些时间来观察内部进程以及这些进程内部的io活动、动态链接库使用情况、打开的句柄、注册表活动。在下面一节中,我们将更清楚地了解nt如何区分操作系统工作占用的cpu时间和应用程序占用的cpu时间,以及nt如何维护和计算中断时间。之所以中断处理是一个非常重要的主题,是因为它不占用任何线程,因而不被显示在任何进程中。换言之,一个拥有繁重中断负荷的系统看起来可能很慢,然而却好像没有进程在运行。我们将在这一节中回答这个问题。
现在,我建议我们最好回过头来讨论花费在操作系统应用中的时间和花费在应用程序代码自身中的时间的界限。nt使用两种内存保护态,它们有时被称为核心态和用户态,或在另外一些场合被称为特权态和用户态。一个进程4gb地址空间中的每一页均被标记出它是否是处于核心态的页。所有系统地址空间中的页均被标记为核心页。所有用户地址空间中的页均被标记位用户页。访问被标记为核心页的页面的唯一途径是运行在核心态,并且只有操作系统和设备驱动程序才能运行在核心态。换言之,除非通过加载设备驱动程序,否则一个用户程序将不能使自己运行在核心态中。这就是在应用程序和操作系统之间提供的内存保护的坚固级别。无论一个应用程序的运行多么不遵循规则,无论它试图引用和改变什么内存地址,它都绝对不会破坏系统数据结构,这是因为所有的操作系统和设备驱动程序内存结构都被标记为核心页。他们处于系统地址空间,同时,因为应用程序运行在用户态,它不可能看到或修改这些数据。
线程经常在用户态和核心态之间进行切换。线程每进行一次系统调用,例如打开一个文件、关闭一个文件、读取数据、写入数据,它便从用户态的应用程序代码变为核心态或操作系统代码。当10毫秒的时钟间隔再次被激发时,nt将如何决定怎样占用cpu时间呢?如果线程正处在核心态或正在运行操作系统的一部分,它将为线程增加特权时间计数器值。然而,如果线程正运行在用户态或应用程序内部,它将使线程占用用户时间。因此,nt精确的跟踪一个线程花费在应用程序中以及花费在操作系统中的时间量。观察任务管理器进程选项卡中的cpu时间列会发现,它并未区分特权时间和用户时间,而仅仅显示了全部cpu时间,但是有一些工具允许我们观察应用程序并迅速计算出这个应用程序分别在程序自身和操作系统中花费了多少时间。它将我们带入了下一个演示:使用qslice或quick slice工具检测进程cpu时间。
让我们通过开始/运行/qslice运行quick slice。它包含在windows 2000支持工具中。quick slice中显示的是进程的cpu活动,其中红色指示为核心态、蓝色指示为用户态。现在请注意在我的系统上发生了什么?quick slice中称为系统进程的0号进程内部100%处于核心态,但在任务管理器中0号进程却被称为系统空闲进程。你可以看到nt进程显示工具中的一个怪癖,对于空闲进程,每个工具创造它们自己的名称并且这些名称并不一致。空闲进程是nt用于统计空闲cpu循环的假冒进程,并且这些cpu循环被计为核心态时间。
让我们运行一个程序来模拟一个通常的用户应用程序。这是一个称作cpustress的程序,它包含在资源工具包中。我将通过开始/运行/cpustress运行该程序。当其开始运行后,缺省的,它将拥有一个运行在低活动级上的线程。低活动级意味着它有25%的时间处于运行状态而另75%的时间处于等待状态。在底部的cpustress程序时常突然出现并在用户态运行一小段时间——表现为蓝色条状图,之后重新变为等待。让我们把它的活动级别变为最大。
单击活动列表框,向下滚动并选择最大。现在请注意发生了什么。变成了100%蓝色。最大活动级别使cpustress陷入了一个无限循环。这里仅有一个进程,因此,它基本上已陷入到应用程序中。且并未产生任何系统调用。如果针对一个进程,我看到一组分开的蓝色和红色,这说明这个程序处于一种通常的情况,即一部分时间花费在应用程序中、一部分时间花费在操作系统中。同样,使用qslice也可以相当容易地观察一个进程并迅速确定进程的时间如何被消耗——是处于用户态应用程序中还是处于核心态操作系统内部。
基于以下三种原因之一,nt会在核心态或特权态下运行操作系统代码,我们将仅描述其中的第一种情况,即用户应用程序发出一个系统调用请求——如打开一个文件、关闭一个文件并释放为其分配的内存、释放内存、创建一个进程、创建一个线程等等。我们还