WINDOWS核心编程-----3章,3.2 内核对象:进程的内核对象句柄表,创建内核对象,关闭内核对象

WINDOWS核心编程-----3章,内核对象:进程的内核对象句柄表,创建内核对象,关闭内核对象

WINDOWS核心编程-----3章,内核对象:进程的内核对象句柄表,创建内核对象,关闭内核对象


3.2 进程的内核对象句柄表

当一个进程被初始化时,系统要为它分配一个句柄表。该句柄表只用于内核对象不用于用户对象或GDI对象。句柄表的详细结构和管理方法并没有具体的资料说明。通常我并不介绍操作系统中没有文档资料的那些部分。不过,在这种情况下,我会进行例外处理,因为,作为一个称职的Windows程序员,必须懂得如何管理进程的句柄表。由于这些信息没有文档资料,因此不能保证所有的详细信息都正确无误,同时,在Windows2000、Windows98和WindowsCE中,它们的实现方法是不同的。为此,请认真阅读下面介绍的内容以加深理解,在此不学习系统是如何进行操作的。表3-1显示了进程的句柄表的样子。可以看到,它只是个数据结构的数组。每个结构都包-个指向内核对象的指针、一个访问屏蔽和一些标志。
在这里插入图片描述

3.2.1 创建内核对象

当进程初次被初始化时,它的句柄表是空的。然后,当进程中的线程调用创建内核对象的函数时,比如CreateFileMapping,内核就为该对象分配一个内存块,并对它初始化。这时,内核对进程的句柄表进行扫描,找出一个空项。由于表 3-1中的句柄表是空的,内核便找到索引1位置上的结构并对它进行初始化。该指针成员将被设置为内核对象的数据结构的内存地址,访问屏蔽设置为全部访问权,同时,各个标志也作了设置(关于标志,将在本章后面部分的继承性一节中介绍)。
下面列出了用于创建内核对象的一些函数(但这决不是个完整的列表):

HANDLE CreateThread(
PSECURITY_ATTRIBUTES pSa
DWORD dwStackSize,
LPTHREAD_START_ROUTINE pfnStartAddr,
PVOID pvParam,
DWORD dwCreationFlags,
PDWORD pdwThreadId);

HANDLE CreateFile(
PCTSTR pszFileName.
DWORD dwDesiredAccess
DWORD dwShareMode,
PSECURITY_ATTRIBUTES pSa,
DWORD dwCreationDistribution,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile):

HANDLE CreateFileMapping(
HANDLE hFile,
PSECURITY_ATTRIBUTES Sa,
DWORD fiProtect.
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
PCTSTR pszName);

HANDLE CreateSemaphore(
PSECURITY_ATTRIBUTES pSa,
LONG lInitialCount,
LONG MaximumCount,
PCTSTR pszName);

用于创建内核对象的所有函数均返回与进程相关的句柄,这些句柄可以被在相同进程中运行的任何或所有线程成功地加以使用。该句柄值实际上是放入进程的句柄表中的索引,它用于标识内核对象的信息存放的位置。因此当调试一个应用程序并且观察内核对象句柄的实际值时会看到一些较小的值,如1,2等。请记住,句柄的含义并没有记入文档资料,并且可能随时变更。实际上在Windows 2000中,返回的值用于标识放入进程的句柄表的该对象的字节数,而不
是索引号本身。每当调用一个将内核对象句柄接受为参数的函数时,就要传递由一个 Create*&函数返回的值。从内部来说,该函数要查看进程的句柄表,以获取要生成的内核对象的地址,然后按定义
得很好的方式来生成该对象的数据结构。如果传递了一个无效索引(句柄),该函数便返回失败,而GetLastError则返回 6(ERROR_INVALID_HANDLE)。由于句柄值实际上是放入进程句柄表的索引,因此这些句柄是与进程相关的,并且不能由其他进程成功地使用。
如果调用一个函数以便创建内核对象,但是调用失败了,那么返回的句柄值通常是0(NULL)。发生这种情况是因为系统的内存非常短缺,或者遇到了安全方面的问题。不过有少数函数在运行失败时返回的句柄值是-1(INVALID_HANDLE_VALUE)。例如,如果CreateFile未能打开指定的文件,那么它将返回INVALID_HANDLE_VALUE,而不是返回NULL。当查看创建内核对象的函数返回值时,必须格外小心。特别要注意的是,只有当调用CreateFile函数时才能将该值与INVALID HANDLE _VALUE进行比较。下面的代码是不正确的:

HANDLE hMutex=CreateMutex(.);
if(hMutex== INVALID HANDLE VALUE){
// We will never execute this code because
//CreateMutex returns NULL if it fails.

同样,下面的代码也不正确

HANDLE hFile=CreateFile(..);
if(hFie == NULL){
// We will never execute this code because CreateFile
//returnS INVALID_HANDLE_VALUE(-1)if it faiis.
3.2.2 关闭内核对象

无论怎样创建内核对象,都要向系统指明将通过调用CloseHandle来结束对该对象的操作:B00L CoseHand1e(HANDLE hobj):
该函数首先检查调用进程的句柄表,以确保传递给它的索引(句柄)用于标识一个进程实际上无权访问的对象。如果该索引是有效的,那么系统就可以获得内核对象的数据结构的地址,并可确定该结构中的使用计数的数据成员。如果使用计数是0,该内核便从内存中撤消该内核对象。如果将一个无效句柄传递给 CloseHandle,将会出现两种情况之一。如果进程运行正常CloseHandle返回FALSE,而GetLastError则返回ERROR INVALID HANDLE。如果进程正在排除错误,系统将通知调试程序,以便能排除它的错误。在CloseHandle返回之前,它会清除进程的句柄表中的项目,该句柄现在对你的进程已经无效,不应该试图使用它。无论内核对象是否已经撤消,都会发生清除操作。当调用C1oseHandle函数之后,将不再拥有对内核对象的访问权,不过,如果该对象的使用计数没有递减为0,那么该对象尚未被撤消。这没有问题,它只是意味着一个或多个其他进程正在使用该对象。当其他进程停止使用该对象时(通过调用 CloseHandle),该对象将被撤消。假如忘记调用 CloseHandle函数,那么会不会出现内存泄漏呢?答案是可能的,但是也不一定。在进程运行时,进程有可能泄漏资源(如内核对象)。但是,当进程终止运行时,操作系统能够确保该进程使用的任何资源或全部资源均被释放,这是有保证的。对于内核对象来说系统将执行下列操作:当进程终止运行时,系统会自动扫描进程的句柄表。如果该表拥有任何无效项目(即在终止进程运行前没有关闭的对象),系统将关闭这些对象句柄。如果这些对象中的任何对象的使用计数降为0,那么内核便撤消该对象。因此,应用程序在运行时有可能泄漏内核对象,但是当进程终止运行时,系统将能确保所有内容均被正确地清除。另外,这个情况适用于所有对象、资源和内存块,也就是说,当进程终止运行时,系统将保证进程不会留下任何对象。

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值