内核对象与句柄的关系

来源:互联网 发布:淘宝上的ica麦片真货 编辑:程序博客网 时间:2024/05/24 01:08

一:一个对象是不是内核对象,通常可以看创建此对象API的参数中是否需要:PSECURITY_ATTRIBUTES 类型的参数。

二:内核对象只是一个内存块,这块内存位于操作系统内核的地址空间,内存块中存放一个数据结构(此数据结构的成员有如:安全描述符、使用计数等)。

三:每个进程中有一个句柄表(handle table),这个句柄表仅供内核对象使用,如下图:


四:解开此中的秘密

比如调用创建一个线程的API:

 HANDLE  hThread ;

DWORD threadId;

 hThread = CreateThread(... , &threadId);

此时发生的事情是:系统查找句柄表,找到一项可用的分配给以上创建的线程;,hThread >> 2 得到句柄在句柄表中的索引(Windows操作系统内部使用hThread最后两位,所以

右移两位得到真正的索引);也是此时内核对象被创建,其数据结构中的引用计数初始为1(这样理解:只要内核对象被创建,其引用计数被初始化为1),这里实则发生两件事:创建了一个内核对象和创建线程的函数打开了此对象,所以内核对象的引用计数加1,这时引用计数就为2了。

当调用CloseHandle(hThread); 时发生这样的事情:系统通过hThread计算出此句柄在句柄表中的索引,然后把那一项处理后标注为空闲可用的项,内核对象的引用计数减1即此时此内核对象的引用计数为1,之后这个线程句柄与创建时产生的内核对象已经没有任何关系了。

问题:我们知道只有当内核对象的引用计数为0时,内核对象才会被销毁,而此时它的引用计数为1,那它什么时候会被销毁?

答:当此线程结束的时候,它的引用计数再减1即为0,内核对象被销毁。此时又有一个新问题产生:我们已经关闭了线程句柄,也就是这个线程句柄已经和内核对象没有瓜葛了,那么那个内核对象是怎么又可以和此线程联系起来了呢? 我觉得是创建线程时产生的那个线程ID,接下来我就证明那个ID与内核对象是有联系的:

请看如下简单的程序:

#include <stdio.h>#include <windows.h>#include <WinBase.h>DWORD WINAPI ThreadProc( LPVOID lpParameter){printf("I am comming...");while (1){} return 0;}int main(){HANDLE hThread;HANDLE headle2;DWORD threadId;hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, &threadId);CloseHandle(hThread);  //  关闭了线程句柄headle2 = OpenThread(THREAD_QUERY_INFORMATION, FALSE, threadId);headle2= OpenThread(THREAD_QUERY_INFORMATION, FALSE, threadId);headle2 = OpenThread(THREAD_QUERY_INFORMATION, FALSE, threadId);return 0;}


图1:


图2:


调试此程序观察headle2的值,可以发现headle2的值是有效的,即函数调用是成功的,证明了线程ID与线程创建当初产生的内核对象是有关系的。

可以猜想:当线程结束时,系统通过线程ID与内核对象联系,使其引用计数减1,为0内核对象被销毁。

ps:观察以上两图,当把CloseHandle(hThread)这一句注释掉,以上程序三次调用OpenThread(),返回值是不相同的,说明返回的句柄不是原来句柄表中原来

的索引项,而是新建了一个内容一样的索引项。让CloseHandle(hThread); 这句也执行,再看看结果:hThread与headle2 的值是一样,证明了系统

管理句柄表的规则:我们把hThread关闭之后,它所在的索引项就被标记为可用;当需要向句柄表中加入新项时会查找第一项可用的索引项,这就是

hThread与headle2 的值是一样的原因。




原创粉丝点击