20170709Windows11_1_内核对象

内核对象:

内核对象概述:

1:Windows操作系统中,内核对象可以通过WinObj工具来查看,WinObj工具下载地址: https://technet.microsoft.com/en-us/sysinternals/winobj,通过这个工具,可以看出Windows里面内核对象的本质是什么。
2:内核对象是属于操作系统的,进程仅仅是拥有内核对象的管理权,WinObj需要以管理员身份运行才可以。可以枚举出系统里面所有的内核对象。

3:Windows中,有一系列的内核对象,Windows这样设计是因为安全问题。R3层主要用于用户使用,是没有监管的,R0层是系统和软件运行的地方,内核对象是系统资源。我们只能通过WinApi发送请求来改变内核对象。当内核对象发生变化就说明函数调用成功了。在进程里面,句柄是属于进程的,但是对应的内核对象还是属于系统的。

设置使用计数的目的:

1:内核对象属于系统,当进程不再使用内核对象的时候,为了节约内存空间,我们应该将内核对象删掉,但是R3层和R0层没有实际的交互,进程和内核对象是物理分隔的,因此设计了使用计数,能够很好的管理内核对象。到最后,使用计数为0之后,在系统空闲的时候就会来清理这些不再使用的内核对象。
2: 如果多个进程都CreateFile打开了一个文件,则他们会使用同一个内核对象,其内核对象的使用计数就会比较大,不能再是某个进程结束了,内核对象就被删掉,而是使用计数为0之后,才可能被删掉。使用计数决定内核对象的生命周期。

安全性设置的作用:

1:使用计数拥有多个进程可以使用同一个内核对象,并且,可以在合适的时候释放内核对象的功能。
2:每一个内核对象里面都有一个安全性的设置,一般是设置成null的,安全性决定哪些用户可以访问,此外,他可以来设置Create内核对象之后,设置了安全性,另一个进程要打开这个内核对象,就必须按照之前设置的安全性来打开。
    eg:在CreateFile的时候会有打开文件方式的设置,以独占还是共享的打开,设置之后,其他进程要打开就必须按照这个要求来打开。在一般操作的时候,对安全性的要求不高,但是在多进程操作一个内核对象的时候,就可能出现安全性的问题。
3:安全性可以决定进程以哪种方式打开内核对象,其他进程只能以哪些方式打开,只能做哪些操作,因此,可以提高安全性。

句柄表及句柄本质:

1:可以尝试在main里面用CreateFile创建两个文件,观察其返回的句柄,可以看出,他们相差4,在创建多个线程的时候,也是一样的,他们都是相差4。
2:在进程里面,有一个进程内核对象,在进程里面的操作都是发出请求,在系统在对进程内核对象做一些操作, 在进程内核对象里面保存了一个句柄表,他是在R0层的,微软只希望我们操作R3层的那些镜像,不希望我们接触到R0层里面的任何东西,是为了提高安全性。R3层的那些镜像都只是虚拟存在的,在R3层随便做什么操作,即使这些镜像发生了不可逆转的后果,都只是影响到R3层,不会对操作系统本事造成影响。
3:句柄表:可以认为就是数组或者链表等,是将一些数据(结构体)综合在一起,结构体里面一般包含:
    1:索引。2:指向内核对象的指针。3:各种标识位。
4:我们使用的句柄实际就是这个索引,索引对应的就是真实的内核对象,但是内核对象的指针不会让任何一个R3层的进程知晓。在进程里面申请内核对象的时候,就会生成对应的索引,实际就是HANDLE(void*),他会以四字节四字节的方式增加,HANDLE只是跟进程相关的,这个HANDLE在其他进程里面都是没有用的,多个进程里面使用的同一个内核对象,他们的HANDLE也是可能不一样的。因为这个索引仅仅是在当前的进程有效,句柄是不可以跨进程使用的,在R3层是没有必要传递内核对象的。不同进程里面指向同一个内核对象的句柄是很可能不一样的。
5:在多进程里面内核对象的公用,例如,A进程创建内核对象,B进程只能打开或者创建内核对象来打开这个内核对象,不能通过传递句柄来实现。

深入理解句柄及索引:

1:进程启动时,进程会创建进程内核对象,分配一些空间, 实际上内核对象分配的这段空间主要就是用于保存句柄表,进程完成初始化后,操作系统会帮我们启动一条MainThread(主线程),启动主线程就会CreateThread创建线程内核对象,这时候就会在我们的句柄表里面找空白的区域,就会将主线程的句柄的地址放入在句柄表里面,这就意味着,索引表里面是固定的, 使用CloseHandle释放内核对象后,再去申请一个内核对象就可能与之前的内核对象的索引值是完全一样的。但是对应的内核对象完全可能不一样。
2:从上面可以看出,CloseHandle可以将索引关闭,创建索引也只是找一块空白空间存放,可以是之前用过并释放了的空间。当然,CloseHandle还可以改变内核对象里面的使用计数。这就是我们在R3层看到的句柄,他仅仅是一个索引和一些数据,不代表内核对象,每个内核对象在不同进程里面也很可能索引不一样。

句柄存在的意义:

1:如果没有CloseHandle,就会导致内核对象的泄漏,浪费很多内存,为了解决这个错误,有了索引表,进程内核对象知道我们使用了哪些内核对象,进程死亡的时候,会一一关闭使用过的内核对象,如果没有其他进程也是用,操作系统就会释放这些内核对象。如果自己CloseHandle,内核对象就会从索引表中移除,进程结束的时候,就不会再关闭这些内核对象了。
2:可以使用ProcessExplore软件来查看系统在当前的进程信息,在View里面设置显示句柄数,可以查看每个进程所使用的句柄数。ProcessExplore工具下载地址: https://technet.microsoft.com/en-us/sysinternals/bb896653
3:操作系统里面一直在维护句柄表,ProcessExplore是一个非常好的查看Windows里面进程信息的工具,可以查看很多进程指标。

内核对象的Signal状态:

1:内核对象里面的一个值:Signal会被设置为FALSE,在进程,线程,事件,标准输入输出流,互斥体,信号,等待计时器,作业 等 内核对象里面,都会有一个Signal内核对象,里面代表受信状态,Signal相当于一个标志,标记了一些状况,进程运行完成之后,Signal状态会变成TRUE,进程内核对象就变成了可受信状态,其他内核对象也会变成一样的。
2:可以使用WaitForSingleObject()来等待内核对象的完成时间,它可以使当前正在执行的线程变成不可调度的状态,他会一直等待指定的内核对象的Signal状态变为TRUE,此时,他就会返回,返回值为多种状态,包括等待超时,等待失败等等。WaiyForMultipleObjects()等到多个内核对象为有信号。
3:WaiyForMultipleObjects()还可以用来判断是哪一个内核对象状态变为可授信状态,使用时不等待所有内核对象,返回值可以体现出是哪一个。WaitForSingleObject会改变内核对象。
4:内核对象可以用于进程和进程之间的同步,守护进程,可以把守护进程设置为父进程,可以进行进程的管理,一个软件可能是由多个进程组成的。

_Wait的三种结果:

1:三种结果分别为等待超时,等待成功,等待失败。
#include <cstdio>
#include <windows.h>
#include <process.h>

unsigned WINAPI ThreadFunc(void* lParam)
{
	Sleep(999);//小于1000,基本都是成功等待完成
	//大于1000,就会变成等待超时。
	return 0;
}

int main()
{

	HANDLE hThread = (HANDLE)_beginthreadex(nullptr, 0, ThreadFunc, nullptr, 0, nullptr);
	//WaitForSingleObject(hThread, INFINITE); //永远等待(30天)
	DWORD dw = WaitForSingleObject(hThread, 1000);//只等待1S
	switch (dw)
	{
	case WAIT_TIMEOUT:
		printf("等待超时\r\n");
		break;
	case WAIT_OBJECT_0:
		printf("成功等待完成\r\n");
		break;
	case WAIT_FAILED://如果将句柄HANDLE直接设置为nullptr,就会是等待失败。
		printf("等待失败\r\n");
		break;
	}

	return 0;
}

_Wait里面的坑:

1:WAIT_OBJECT_0等宏实际上是给WAitForMultipleObject来使用的,用于返回是哪个内核对象完成:
#include <cstdio>
#include <windows.h>
#include <process.h>

unsigned WINAPI ThreadFunc(void* lParam)
{
	Sleep((int)lParam);//小于1000,基本都是成功等待完成
	//大于1000,就会变成等待超时。
	return 0;
}

int main()
{
	HANDLE hThread[2] = { INVALID_HANDLE_VALUE };
	hThread[0] = (HANDLE)_beginthreadex(nullptr, 0, ThreadFunc, (void*)200, 0, nullptr);
	hThread[1] = (HANDLE)_beginthreadex(nullptr, 0, ThreadFunc, (void*)500, 0, nullptr);

	bool bLoop = true;
	DWORD dwret = -1;
	while (bLoop)
	{
		dwret = WaitForMultipleObjects(2, hThread, FALSE, 100);//永远检测不到二号线程。
		switch (dwret)//两个线程都结束的时候,返回值还是为0,这就会导致一直为WAIT_OBJECT_0
		{
		case WAIT_TIMEOUT:
			printf("Wait TimeOut, Next Check...\r\n");
			break;
		case WAIT_OBJECT_0:
			printf("Thread 0 is Ok...\r\n");
			break;
		case (WAIT_OBJECT_0 + 1):
			printf("Thread 1 is Ok...\r\n");
			bLoop = false;
			break;
		case WAIT_FAILED:
			printf("Error...\r\n");
			break;
		}
	}

	return 0;
}

2:当多个内核对象都结束的时候,其返回值一直就是WAIT_OBJECT_0,这使得我们无法判断最后一个结束的是哪个。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值