《Windows via C/C++》学习笔记 —— 内核对象

最近刚开始学习《Windows via C/C++》的时候,开始的时候没有做好笔记,所以学得很模糊,现在利用在休息的机会,好好学习和进修《Windows via C/C++》时,想边学边做一些学习的笔记,以为自己的学习提供帮助,和以后自己查看、温习和提高。

《Windows via C/C++》的前两章,本人觉得是没什么好多讲的,看看就可以了,理解起来并不困难,只要知道有这么个东西,以后编程时注意养成习惯就可以了,更何况,这些东西,我们平常也是在用的。

 

笔者就从第3章——“内核对象”开始做学习笔记吧:

 

众所周知,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()函数来重新设置内核对象的句柄位。

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

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

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

关于内核对象,就介绍到这里。

posted on 2008-07-29 17:53  小虎无忧  阅读(957)  评论(0编辑  收藏  举报

导航