playerken

博客园 首页 新随笔 联系 订阅 管理

每个内核对象只是内核分配的一个内存块,并且只能由该内核访问。当调用一个用于创建内核对象的函数时,该函数就返回一个用于标识该对象的句柄。该句柄可以被视为一个不透明值,你的进程中的任何线程都可以使用这个值。为了使操作系统变得更加健壮,这些句柄值是与进程密切相关的。因此,如果将该句柄值传递给另一个进程中的一个线程(使用某种形式的进程间的通信)那么这另一个进程使用你的进程的句柄值所作的调用就会失败。

 

内核对象的使用计数

内核对象由内核所拥有,而不是由进程所拥有。每个对象包含一个使用计数。当一个对象刚刚创建时,它的使用计数被置为1。然后,当另一个进程访问一个现有的内核对象时,使用计数就递增1。当进程终止运行时,内核就自动确定该进程仍然打开的所有内核对象的使用计数。如果内核对象的使用计数降为0,内核就撤消该对象。

 

进程的内核对象句柄表

当一个进程被初始化时,系统要为它分配一个句柄表。该句柄表只用于内核对象。用于创建内核对象的所有函数均返回与进程相关的句柄,这些句柄可以被在相同进程中运行的任何或所有线程成功地加以使用。该句柄值实际上是放入进程的句柄表中的索引,它用于标识内核对象的信息存放的位置。

 

创建内核对象

若要确定一个对象是否属于内核对象,最容易的方法是观察创建该对象所用的函数。创建内核对象的所有函数几乎都有一个参数,你可以用来设定安全属性的信息。

 

关闭内核对象

无论怎样创建内核对象,都要向系统指明将通过调用CloseHandle来结束对该对象的操作。该函数首先检查调用进程的句柄表,以确保传递给它的索引(句柄)用于标识一个进程实际上无权访问的对象。如果该索引是有效的,那么系统就可以获得内核对象的数据结构的地址,并可确定该结构中的使用计数的数据成员。如果使用计数是0,该内核便从内存中撤消该内核对象。

在CloseHandle返回之前,它会清除进程的句柄表中的项目,该句柄现在对你的进程已经无效,不应该试图使用它。无论内核对象是否已经撤消,都会发生清除操作。当调用
CloseHandle函数之后,将不再拥有对内核对象的访问权,不过,如果该对象的使用计数没有递减为0,那么该对象尚未被撤消。这没有问题,它只是意味着一个或多个其他进程正在使用该对象。当其他进程停止使用该对象时(通过调用CloseHandle),该对象将被撤消。

假如忘记调用CloseHandle函数,那么会不会出现内存泄漏呢?答案是可能的,但是也不一定。在进程运行时,进程有可能泄漏资源(如内核对象)。但是,当进程终止运行时,操作系统能够确保该进程使用的任何资源或全部资源均被释放,这是有保证的。对于内核对象来说,系统将执行下列操作:当进程终止运行时,系统会自动扫描进程的句柄表。如果该表拥有任何无效项目(即在终止进程运行前没有关闭的对象),系统将关闭这些对象句柄。如果这些对象中的任何对象的使用计数降为0,那么内核便撤消该对象。因此,应用程序在运行时有可能泄漏内核对象,但是当进程终止运行时,系统将能确保所有内容均被正确地清除。另外,这个情况适用于所有对象、资源和内存块,也就是说,当进程终止运行时,系统将保证进程不会留下任何对象。

 

进程间共享内核对象 - 对象句柄的继承

父进程创建内核对象时可以指定对象句柄为可继承。当创建子进程时可以指定子进程继承父进程中的可继承句柄。子进程在创建时会遍历父进程的句柄表,将可继承句柄复制到自己的句柄表中,递增内核对象使用计数。如果要撤消内核对象,那么父进程和子进程必须调用该对象上的CloseHandle函数,也可以终止进程的运行。

对象句柄的继承性只有在生成子进程的时候才能使用。如果父进程准备创建带有可继承句柄的新内核对象,那么已经在运行的子进程将无法继承这些新句柄。

 

进程间共享内核对象 - 命名对象

互斥,事件,信号量,文件映射……

当第二个进程企图创建一个命名内核对象时,发现一个已有的同名的并且类型一样的内核对象,便在自己的句柄表中添加该内核对象。

当你的多个内核对象拥有相同的名字时,有一个非常重要的细节必须知道。当Process B调用CreateMutex时,它将安全属性信息和第二个参数传递给该函数。如
果已经存在带有指定名字的对象,那么这些参数将被忽略。应用程序能够确定它是否
确实创建了一个新内核对象,而不是打开了一个现有的对象。方法是在调用Create*函数后立即调用GetLastError,检查返回值是否为ERROR_ALREADY_EXISTS。

调用Create*函数与调用Open*函数之间的主要差别是,如果对象并不存在,那么Create*函数将创建该对象,而Open*函数则运行失败。

命名对象可以被用来防止运行一个应用程序的多个实例:

1. 使用GUID作为内核对象的名字。

2. 使用Create*创建内核对象,调用GetLastError检查返回值,若为ERROR_ALREADY_EXISTS则return。

终端服务器拥有内核对象的多个名字空间。服务程序的名字空间对象总是放在全局名字空间中。按照默认设置,在终端服务器中,应用程序的命名内核对象将放入会话的名字空间中。但是,如果将“Global\”置于对象名的前面,就可以使命名对象进入全局名字空间。也可以显式说明想让内核对象进入会话的名字空间,方法是将“Local\”置于对象名的前面。

 

进程间共享内核对象 - 复制对象句柄

共享跨越进程边界的内核对象的最后一个方法是使用DuplicateHandle函数,该函数取出一个进程的句柄表中的项目,并将该项目拷贝到另一个进程的句柄表中。

posted on 2011-08-23 22:22  playerken  阅读(298)  评论(0编辑  收藏  举报