回炉重造之重读Windows核心编程-003-内核对象
内核对象是个比较难理解的概念,问题的根源就在于即使是《核心编程》书中也没有说清楚它的定义,只是不停地举例和描述它的性质,还有如何使用。
盲人摸象,难见全貌。只能尽可能列举它的性质,注意使用了。
引用计数(书中的说法是使用计数)就是内核对象的一个很关键的性质。由于内核对象的拥有者是内核而不是进程,所以只能由内核来做撤销内核对象的操作。而通常一个内核对象不一定只被一个进程使用的,创建或者撤销内核对象,就要看引用计数了。引用计数在内核对象被创建的时候被置为1,被进程访问一次引用计数就递增1。当引用计数降为0,内核就撤销这个内核对象。(至于何时引用计数递减1,书中没有明确说明,不过有编程经验的你肯定知道是什么时候了)
安全性也是内核对象的一个重要的性质,它用于描述内核对象的访问者。由于创建内核对象的时候几乎都会有一个参数,指向Security_Attributes结构体的指针,例如CreateFileMapping(定义不详)。如果这个指针是NULL值,那就是默认的安全性,只有管理对象的小组成员和创建者可以访问;不过,只要你初始化并操作lpSecurityDescriptor这个成员,就可以设置它的安全性了。这样,内核对象被访问的时候(例如OpenFileMapping),在返回一个有效的句柄之前会执行一次安全检查,如果不通过检查返回的就不是一个有效句柄,而是NULL了,它的LastError是5(ERROR_ACCESS_DENIED)。
进程的内核对象句柄表,在进程被创建的时候被分配。当线程共有内核对象产生的时候,内核会在句柄表中找出一个空项,把内核对象的内存块指针写进去。
如果一个线程中调用函数返回一个句柄,这个句柄可以也只可以被线程中的所有线程使用。这些句柄的值实际上是句柄在当前进程句柄表中的索引,但不是固定的,如在Win2k中返回的句柄是用于标识句柄表的该对象的字节数。如果给句柄表中传入一个无效值,GetLastError则会返回6(ERROR_INVALID_HANDLE)。
如果调用函数创建内核对象失败了,那么句柄的值通常是0(NULL),也有些函数返回的是INVALID_HANDLE_VALUE。但是无论用什么方式创建内核对象,都要通过CloseHandle来结束对对象的操作。这个函数会先检查句柄表,确定传入的索引是否有效,并查看引用计数,如果是0则撤销这个对象。
一个无效的句柄传给CloseHandle的话,GetLastError会返回ERROR_INVALID_HANDLE。如果进程正在被调试,则通知调试器,以确定这个错误。
加入忘记调用CloseHandle,有可能会产生资源泄漏。因为进程终止的时候,系