内核对象

来源:互联网 发布:考勤机网络连接 编辑:程序博客网 时间:2024/04/30 06:40

众所周知,Windows 编程最基础的还是对资源的利用、控制操作,资源是一个比较具体的东西,比如 CPU、内存等等。那么怎么来对资源进行操作呢,我们通常是通过“对象”与“资源”去打交道(这是本人现在的理解,可能有误)。而对象又可以分为“用户对象”、“GDI 对象”和“内核对象”。

显然被称作“内核对象”,他肯定是只能被内核访问的创建于内存中的“数据结构”,也就是说,内核对象只是一块存储空间,这个空间中保存了有关这个“对象”的“数据结构”,简而言之,就是一个数据结构,只能被操作系统内核访问。对于各种不同类型的内核对象,只能被内核访问,那我们又怎么来操作这些存在内存中的内核结构呢?

微软为我们提供了“句柄”,就可以使我们很方便的来通过句柄操作内核对象了。因此在每一个内核对象被创建的时候,我们都可以得到一个唯一标识该内核对象的句柄值。也就是通过这个句柄,然后调用一些 API 函数,对这个句柄所标识的内核对象进行访问和修改,这个句柄称为“内核对象句柄”,相当于内核对象的“身份证”。

讲了这么多,还没有提到内核对象到底是怎么样一个数据结构呢。微软并没有给出明确的数据结构,但是一个内核结构他肯定具有两个很重要的属性:1、内核对象使用计数器,每当一个内核对象被创建时,系统都会创建这个使用计数,并且初始为1,当这个使用计数减为0的时候,那么这个内核对象就被从内存中释放掉,也就不复存在了。2、内核对象安全性描述,关于这个描述,微软为我们提供了一个 SECURITY_ATTRIBUTE 结构,专门在创建内核对象时设置该内核对象的安全性描述。这里我们可以提供一条用来判断一个对象是否为内核对象的依据,即如果在创建对象句柄的函数参数中包含了 SECURITY_ATTRIBUTE 结构的参数,那么该对象肯定是内核对象。

内核对象是在“进程”中创建的,所以在一个进程中都有一份与内核对象有关的“进程句柄表”,所有在这个进程中创建或打开的内核对象的句柄都会出现在这个进程句柄表中,一旦这些句柄被关闭,其引用计数为0的时候它所标识的内核对象被操作系统释放,也就是删除,这个句柄也由操作系统自动将从进程句柄表中删除,释放内存空间。

也正是由于这个原因,同一个进程中的所有“线程”都可以访问该进程中的所有“内核对象句柄”(当然被关闭了的句柄也就不被称为内核对象句柄了)。

“进程句柄表”“可能”有几个重要的属性:1、一个指向对象内存块的指针(一个地址);2、访问标志;3、对象属性标志。

以上我们提到了,内核对象句柄是进程的句柄,那么有很多情况下我们需要在不同进程运行的线程能够共享一些内核对象,就很难得到了实现了。微软也考虑了这种情况,为此为我们提供了3种方法可以实现跨进程边界的共享内核对象,也可以说是三种情况下可以进行共享内核对象的可能操作吧。

第一种方法,就是当父进程在创建子进程前,将那些需要被子进程继承的内核对象句柄的标志位设置为1(通过创建内核对象时将 SECURITY_ATTRIBUTE 结构中的 bInheritHandle 置为 TRUE 就可以了),然后在创建子进程的函数中把 bInheritHandle 同样设置为 TRUE,就实现了需要继承的内核对象被子进程继承的功能(同时子进程得到一份继承句柄的拷贝项加入自己的内核对象句柄表,同时这些内核对象的使用计数都加1)。这样在子进程中就可以使用父进程中的内核对象句柄了。

注意:在这里我们只是说在创建内核句柄的时候设置内核对象句柄的标志位,其实我们也可以调用 SetHandlInformation() 函数来重新设置内核对象的句柄位。

第二种方法,通过给对象命名的方法来实现,即我们在创建内核对象时给它一个字符串作为唯一标识该内核对象,因为非终端服务器类型的系统,其名字空间只有一个,所以在一个进程中用以上方法创建了一个内核对象,如果在其它进程再创建同一个名字的内核对象(需要前一进程创建的内核对象还未被内存释放),则在前后两个进程中就可以实现对该内核对象的共享了。以上说的是 Create* 函数方法,其实我们也可以采用 Open* 函数方法。实质跟 fileOpen 函数具有类似的功效,不过一个对于文件来说,而前者则是对于内核对象来说而已。

第三种方法,通过 DuplicateHandle() 函数来实现,至于这个函数的参数较多,并且他的功能更为强大,所以书本上给我们罗列了三个例子,来详细说明了三种情况的应用。在这里笔者觉得看书上例子就可以了。

自然了,以上创建和共享了内核对象的句柄,本来就是用来使用这些句柄的,来对具体的对象进行操作的,在这里不作展开了。一旦一个内核对象在一个进程中用完之后,我们必须记得关闭该句柄,以使得内核对象的引用计数减1,从而实现该内核对象不再被访问时能够顺利的释放内存,我们可以使用传递内核对象的句柄给 CloseHandle(HANDLE hHandle) 函数来关闭一个内核对象。不必等到所有程序都关闭时才释放,这是对资源的一种浪费,同时还有可能造成资源的泄漏。

原创粉丝点击