1、什么是内核对象作为一名 Windows 软件开发者,你经常要创建,打开,并且使用内核对象。系统建立并且使用几类内核对象,例如进入标志对象,事件对象,文件对象,文件绘制对象, I/O 完成接口对象,工作对象,mailslot 对象,mutex 对象,管道对象,进程对象,信号物体,线程对象和可等待的定时器对象。 这些对象通过调用各种各样的函数来建立。 例如,CreateFileMapping 函数使系统建立一个文件绘制对象。 每个内核对象都只是内核分配的一个存储块,而且只有通过内核才可以进入。 这个存储块是关于对象的信息的数据结构。 一些成员( 安全性描述符,使用记数等等 )相同地通过所有对象类型
2、,但是大多数成员明确一种特定的对象类型。 例如, 一个进程对象有一个进程 ID, 基本优先级和一段退出代码,而一个文件对象有一个字节补偿,一种分享方式和一种打开方式。因为内核对象的数据结构只通过内核可以进入,对于应用者来说,在存储器里找到这些数据结构并且直接改变他们的内容是不可能的。微软公司谨慎地采用这个限制以保证内核对象结构始终保持一致的状态。 这个限制也允许微软公司增加,删除,或者改变这些结构中的成员而对应用没有任何影响。如果我们不能直接改变这些结构,我们在应用中怎样操作这些内核对象? 方法是Windows 提供一组明确定义的如何使用这些结构的函数集。通过这些函数总可以进入内核对象。当你调
3、用函数来建立一个内核物体时,这个函数返回一个确认这个对象的句柄。 把这个句柄作为一个能在你的进程中的任何线程中使用的不透明的表。 你向各种窗口函数传递这个句柄,因此系统知道你想要操作哪个内核对象。我们在这章里将谈论更多有关这些句柄的内容。为了使操作系统健壮,这些句柄表是关于过程的。所以,如果你传递这个句柄表给另一个进程中的一个线程(使用某种形式的进程间通信) ,另外的这个进程将使你使用的句柄表失败。在“跨越进程边界共享内核对象 “(这章的最后)这部分里,我们将看到允许多个过程成功地分享单个内核对象的 3 个机制。7.7 线程的优先级在这章的开头,先解释了在调度程序把另一个可调度的线程分配给 C
4、PU 之前,CPU 怎样能运行一个只有大约 20 毫秒的线程。这种情况发生在如果所有的线程有相同的优先级的前提下,但是实际上线程被分配许多不同的优先级,这影响调度程序选择哪一个线程作为要运行的下一个线程。每个线程被分配到从 0(最低的 )到 31(最高的) 的优先级数。当系统决定分配 CPU 给哪个线程的时候,它首先检查优先级数为 31 的线程,并且把这些线程循环排成队列。如果优先级数为 31 的线程是可调度的,则分配 CPU。在这个线程时间片的最后,系统检查是否有其他优先级数为 31 的线程可以运行,如果有的话允许这个线程分配到 CPU 。一旦优先级数为 31 的线程可以被调度,系统则不会把
5、 CPU 分配给任何一个优先级数从 0 到 30 的线程。这种状态被称为饥饿状态。当高优先级的线程占用很多的 CPU 时间以致于低优先级的线程没机会执行时,饥饿发生。饥饿很少发生在多处理机的机器上,因为在这种机器上,一优先级数为 31 的线程和一优先级数为 30 的线程能同时运行。系统总是努力保持 CPU 繁忙,只有没有线程可调度的时候,CPU 才会空闲。你可能会认为象这样设计的系统中,低优先级的线程永远没有机会运行。但是我已经指出,在同一时刻,系统中的大多数线程是不可调度的。例如,如果你的进程的主要线程叫 GetMessage,并且系统没有发现有待解决的消息,则系统挂起你进程中的线程,放弃线
6、程时间片的剩余部分,并且立即把 CPU 分配给另一个正等待的线程。如果没有出现恢复 GetMessage 运行的消息,则该进程的主要线程始终保持挂起状态,并且永远不会分配到 CPU。但是,当一条消息被放置在线程队列中时,如果没有更高优先级的线程需要执行,系统就会知道这个线程不应该再挂起,并且分配 CPU。 )再来看另一个问题。不管低优先级的线程是否正在运行,高优先级的线程总是抢先于低优先级的线程。例如,一优先级数为 5 的线程正在运行,同时系统确定一高优先级的线程准备运行,则系统立即挂起低优先级的线程(即使在它的时间片期间) 并且把 CPU 分配给高优先级的线程,而且这个高优先级的线程将得到一
7、个完整的时间片。顺便提一下,系统 boots 创建的一个特殊的线程叫零页线程。这个线程分配到的优先级数为 0,而且在整个系统中这是唯一一个运行于优先级数为 0 的线程。零页线程负责当没有其他线程需要完成工作的时候,调整在系统中 RAM 的免费页。19.1 DDL 与进程的地址空间建立一个 DLL 比建立应用更容易,因为一个 DLL 通常由一组任何应用都能使用的自动函数组成。通常在 DDL 中没有对处理消息循环或者创建窗口的支持代码。一个 DLL 仅仅是一组源代码模块,每个模块包含一个应用(可执行文件 )或者另一个 DLL 将调用的一组函数。尽管源代码文件已经被编译,他们被连接程序连接,就象一个
8、可执行的应用文件一样。但是,对于每个 DDL,你必须具体指定 DLL 转移到连接程序。 这个转移使连接程序向由此产生的 DLL 文件图象发出一些不同的信息,以便操作系统的装载者确认这个文件图象是一个 DLL,而不是应用。在一个应用(或者另一个 DLL)在一 DLL 中能调用函数之前,该 DLL 的文件图象必须被写入调用进程的地址空间。 你能使用下列两种方法之一来实现:在加载时隐式连接或者在运行时显式连接。隐式连接在这章后面将会讨论;显式连接会在第 20 章讨论。一旦一个 DLL 的文件图象被写入调用进程的地址空间,DLL 的函数对所有在进程中的线程均可使用。实际上,DLL 几乎失去了所有作为一
9、 DLL 的特性:对进程中的线程来说,DLL 的代码和数据看起来象发生在进程的地址空间里的附加代码和数据。当一个线程调用一个 DLL 的函数时,这个 DLL 函数观察这个线程的堆栈以收回它的传递参数并且为它需要的任何本地变量使用这个线程的堆栈。另外,任何在 DLL 函数中用代码创建对象都是属于调用线程或进程的一个 DLL 从未拥有任何东西。例如, 如果在一 DLL 中有一个称为 VirtualAlloc 的函数,地址空间的区域被调用线程所在进程的地址空间保留。如果 DLL 过后未写入进程的地址空间,地址空间区域仍被保留,因为系统不跟踪在 DLL 中的函数保留区域的事实。保留的区域是属于进程的,并且只有一个线程设法调用 VirtualFree 函数或者进程终止时才释放。正如你所知,一个可执行文件的全局和静态变量在多次运行的相同的可执行实例间是不共享的。Windows 98 保证当可执行文件被写入进程的地址空间时,分配存储空间给可执行文件的全局和静态变量;Windows 2000 通过使用在第 13 章讨论的 copy-on-write 机制保证这一点。 在一 DLL 中的全局和静态变量以完全相同的方式处理。当一个进程将一DLL 图象文件写入它的地址空间,系统也同时创建全局和静态数据变量的实例。
Copyright © 2018-2021 Wenke99.com All rights reserved
工信部备案号:浙ICP备20026746号-2
公安局备案号:浙公网安备33038302330469号
本站为C2C交文档易平台,即用户上传的文档直接卖给下载用户,本站只是网络服务中间平台,所有原创文档下载所得归上传人所有,若您发现上传作品侵犯了您的权利,请立刻联系网站客服并提供证据,平台将在3个工作日内予以改正。