1. 内核对象
Windows中每个内核对象都只是一个内存块,它由操作系统内核分配,并只能由操作系统内核进行访问,应用程序不能在内存中定位这些数据结构并直接更改其内容。这个内存块是一个数据结构,其成员维护着与对象相关的信息。少数成员(安全描述符和使用计数)是所有内核对象都有的,但大多数成员都是不同类型对象特有的。
CreateFile
如:file文件对象、event事件对象、process进程、thread线程、iocompletationport完成端口(windows服务器)、mailslot邮槽、mutex互斥量和 registry注册表 等
2. 内核对象的使用计数与生命期
内核对象的所有者是操作系统内核,而非进程。换言之也就是说当进程退出,内核对象不一定会销毁。操作系统内核通过内核对象的使用计数,知道当前有多少个进程正在使用一个特定的内核对象。初次创建内核对象,使用计数为1。当另一个进程获得该内核对象的访问权之后,使用计数加1。如果内核对象的使用计数递减为0,操作系统内核就会销毁该内核对象。也就是说内核对象在当前进程中创建,但是当前进程退出时,内核对象有可能被另外一个进程访问。这时,进程退出只会减少当前进程对引用的所有内核对象的使用计数,而不会减少其他进程对内核对象的使用计数(即使该内核对象由当前进程创建)。那么内核对象的使用计数未递减为0,操作系统内核不会销毁该内核对象。示例如下:
(1)进程1退出,2不退出时。内核对象A,B的引用计数减为0,被操作系统内核销毁,而进程1只减少自身对C,D的引用计数,不会影响进程2对C,D的引用计数,此时C,D引用计数不为0,不会被销毁。
(2)进程2退出,1不退出时。进程2减少自身对C,D的引用计数,不会影响进程1,故A,B,C,D都不会被销毁
(3)进程1,2均退出时,只要ABCD不被别的进程使用,内核对象A,B,C,D的引用计数均递减为0,被内核销毁
(4)进程1和2均为退出时,内核对象A,B,C,D的引用计数只要有一个递减为0,那么递减为0的内核对象便被内核销毁
3. 操作内核对象
Windows提供了一组函数进行操作内核对象。成功调用一个创建内核对象的函数后,会返回一个句柄,它表示了所创建的内核对象,可由进程中的任何线程使用。在32位进程中,句柄是一个32位值,在64位进程中句柄是一个64位值。我们可以使用唯一标识内核对象的句柄,调用内核操作函数对内核对象进行操作。
4. 内核对象与其他类型的对象
Windows进程中除了内核对象还有其他类型的对象,比如窗口,菜单,字体等,这些属于用户对象和GDI对象。要区分内核对象与非内核对象,最简单的方式就是查看创建这个对象的函数,几乎所有创建内核对象的函数都有一个允许我们指定安全属性的参数。
注意:
1 一个对象是不是内核对象,通常可以看创建此对象API的参数中是否需要:PSECURITY_ATTRIBUTES 类型的参数。
2 内核对象只是一个内存块,这块内存位于操作系统内核的地址空间,内存块中存放一个数据结构(此数据结构的成员有如:安全描述符、使用计数等)。
3 每个进程中有一个句柄表(handle table),这个句柄表仅供内核对象使用,如下图:
4 调用
hThread = CreateThread(... , &threadId);
当调用了CreateThread CreateFile 等创建内核对象的函数后,
就是相当于操作系统多了一个内存块,这个内存块就是内核对象
也是此时内核对象被创建,其数据结构中的引用计数初始为1(这样理解:只要内核对象被创建,其引用计数被初始化为1),这里实则发生两件事:创建了一个内核对象和创建线程的函数打开(访问)了此对象,所以内核对象的引用计数加1,这时引用计数就为2了。
调用API CreateThread的时候,不仅仅是创建了一个内核对象,引用计数+1,还打开了内核对象+1,所以引用计数变为2
当调用CloseHandle(hThread); 时发生这样的事情:系统通过hThread计算出此句柄在句柄表中的索引,然后把那一项处理后标注为空闲可用的项,内核对象的引用计数减1即此时此内核对象的引用计数为1,之后这个线程句柄与创建时产生的内核对象已经没有任何关系了。不能通过hThread句柄去访问内核对象了
只有当内核对象的引用计数为0时,内核对象才会被销毁,而此时它的引用计数为1,那它什么时候会被销毁?
当此线程结束的时候,它的引用计数再减1即为0,内核对象被销毁。此时又有一个新问题产生:我们已经关闭了线程句柄,也就是这个线程句柄已经和内核对象没有瓜葛了,那么那个内核对象是怎么又可以和此线程联系起来了呢? 其实是创建线程时产生的那个线程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;
}