由于内核对象的数据结构只能由操作系统内核访问,所以应用程序不能在内存中定位这些数据结构并直接更改其内容。Microsoft有意强化了这个限制,确保内核对象结构保持一致性状态。正是因为有这个限制,所以Microsoft能自由地添加、删除或修改这些结构中的成员,同时不会干扰应用程序的正常运行。
既然不能直接更改这些结构,应用程序应该如何操纵这些内核对象呢?答案是利用Windows提供的一组函数,这组函数会以最恰当的方式来操纵这些结构。我们始终可以使用这些函数来访问这些内核对象。调用一个会创建内核对象的函数后,函数会返回一个句柄(handle),它标识了所创建的对象。可以将这个句柄想象为一个不透明(opaque)的值,它可由进程中的任何线程使用。在32位Windows进程中,句柄是一个32位值;在64位Windows进程中,则是一个64位值。为了让操作系统知道我们要对哪个内核对象进行操作,我们需要将这个句柄传给各种Windows函数。
为了增强操作系统的可靠性,这些句柄值是与进程相关的。所以,如果将句柄值传给另一个进程中的线程(通过某种进程间通信方式),那么另一个进程用我们的进程的句柄值发出调用时,就可能失败;甚至更糟糕的是,它们会根据句柄在我们的进程句柄表的索引来引用另一个进程中的完全不同的内核对象。
使用计数
内核对象的所有者是操作系统内核,而非进程。换言之,如果我们的进程调用一个函数来创建了一个内核对象,然后进程终止运行,则内核对象并不一定会销毁。大多数情况下,这个内核对象是会销毁的,但假如另一个进程正在使用我们的进程创建的内核对象,那么在其他进程停止使用它之前,它是不会销毁的。总之,内核对象的生命周期可能长于创建它的那个进程。
操作系统内核知道当前有多少个进程正在使用一个特定的内核对象,因为每个对象都包含一个使用计数(usage count)。使用计数是所有内核对象类型都有的一个数据成员。初次创建一个对象的时候,其使用计数被设定为1。另一个进程获得对现有内核对象的访问后,使用计数就会递增。如果进程终止运行后,操作系统内核将自动递减此进程仍然打开的所有内核对戏的使用计数。如果一旦对象的使用计数变成0,操作系统内核就会销毁该对象。这样一来,可以保证系统中不存在没有被任何进程引用的内核对象。
内核对象的安全性
内核对象可以用一个安全描述符(security descriptor, SD)来保护。安全描述符描述了谁(通常是对象的创建者)拥有对象;哪些组和用户被允许访问或使用此对象;哪些组和用户被拒绝访问此对象。安全描述符通常在编写服务器应用程序的时候使用。
typedef struct _SECURITY_ATTRIBUTES
内核对象,用户对象或GDI对象
几乎所有创建内核对象的函数都有一个允许我们指定安全属性信息的参数。