内核对象

内核对象
1、什么是内核对象?
内核对象的数据结构只能由内核访问。
他们有:令牌(access token)对象、事件对象、文件对象、文件映射对象、I/O完成端口对象、作业对象、mailslot对象、mutex对象、pipe对象、进程对象、semaphore对象、线程对象、waitable timer对象以及thread pool worker factory对象等等。大多数成员都是不同的对象类型特有的。
2、生命周期。
取决与其计数:
内核对象的所有者是内核,而非进程。换言之,如果你的进程调用一个函数来创建了一个内核对象,然后进程终止运行,则内核对象并不一定会销毁。大多数情况下,这个内核对象是会销毁的,但假如另一个进程正在使用你的进程创建的内核对象,那么在其他进程停止使用它之前,它是不会销毁的。总之,内核对象的生命期可能长于创建它的那个进程。
内核知道当前有多少个进程正在使用一个特定的内核对象,因为每个对象都包含一个使用计数(usage count)。

使用计数是所有内核对象类型都有的一个数据成员。初次创建一个对象的时候,其使用计数被设为 1。另一个进程获得对现有内核对象的访问后,使用计数就会递增。进程终止运行后,内核将自动递减此进程仍然打开的所有内核对象的使用计数。一个对象的使用计数变成0,内核就会销毁该对象。这样一来,可以保证系统中不存在没有被任何进程引用的内核对象。

3、管理内核对象
一个进程在初始化时,系统将为它分配一个句柄表。一个进程的句柄表,它只是一个由数据结构组成的数组。每个结构
都包含指向一个内核对象的指针、一个访问掩码(access mask)和一些标志。
(1)、创建一个内核对象
一个进程首次初始化的时候,其句柄表为空。当进程内的一个线程调用一个会创建内核对象的函数时,内核将为这个对象分配并初始化一个内存块。然后,内核扫描进程的句柄表,查找一个空白的记录项(empty entry)。指针成员会被设置成内核对象的数据结构的内部内存地址,访问掩码将被设置成拥有完全访问权限,标志也会设置。

如果创建调用失败,那么返回的句柄值通常为0(NULL)。之所以失败,可能是由于系统内存不足,或者遇到了一个安全问题。遗憾的是,有几个函数在调用失败时会返回句柄值–1。所以要看清楚再判断。
(2)、关闭内存对象
无论以什么方式创建内核对象,都要调用CloseHandle向系统指出你已经结束使用对象:
BOOL CloseHandle(HANDLE hobject);
结束时需要注意::;
   ①、在内部,该函数首先检查主调进程的句柄表,验证“传给函数的句柄值”标识的是“进程确实有权访问的一个对象”。如果句柄是有效的,系统就将获得内核对象的数据结构的地址,并在结构中递减“使用计数”成员。如果使用计数变成0,内核对象将被销毁,并从内存中删除。
   ②、一旦调用CloseHandle,你的进程就不能访问那个内核对象。
   ③、如果对象的使用计数没有递减至0,它就不会被销毁。它表明另外 还有一个或多个进程在使用该对象。当其他进程全部停止使用这个对象后(通过调用CloseHandle),对象就会被销毁。
   ④、在创建一个内核对象时,我们会将它的句柄保存到一个变量中。将此变量作为参数调用了CloseHandle函数后,还应同时将这个变量设为NULL。
   ⑤、当进程终止运行,操作系统会确保此进程所使用的所有资源都被释放!对于内核对象,操作系统执行的是以下操作:进程终止时,系统自动扫描该进程的句柄表。如果这个表中有任何有效的记录项(即进程终止前没有关闭的对象),操作系统会为你关闭这些对象句柄。任何这些对象的使用计数递减至0,内核就会销毁对象。

(3)、进程共享内核对象、
很多地方需要用到进程共享内核对象。例如
①、利用文件映射对象,可以在同一台机器上运行的两个不同进程 之间共 享数 据块。  
②、借助mailslots(信箱)和named pipes(匿名管道),在网络中的不同计算机上运行的进程可以相互发送数据块。
③、mutexes(互斥对象)、semaphores(信标对象)和事件允许不同进程中的线程同步执行。例如,一个应用程序可能需要在完成某个任务之后,向另一个应用程序发出通知。

三种不同的机制来允许进程共享内核对象: 使用对象句柄继承;为对象命名;以及复制对象句柄。
  1 使用对象句柄继承
设置可继承标志为1。对象句柄的继承只会在生成子进程的时候发生。
程序初始化时会为父进程创建一个进程句柄表,使用对象句柄继承,系统会为子进程创建一个新的、空白的进程句柄表——就像它为任何一个新进程所做的那样。系统会遍历父进程的句柄表,对它的每一个记录项进行检查。凡是包含一个有效的“可继承的句柄”的项,都会被完整地拷贝到子进程的句柄表。在子进程的句柄表中,拷贝项的位置与它在父进程句柄表中的位置是完全一样的。这是非常重要的一个设计,因为它意味着:在父进程和子进程中,对一个内核对象进行标识的句柄值是完全一样的。内核对象的使用计数将递增。
2、改变句柄的标志
 父进程创建了一个内核对象,得到了一个可继承的句柄,然后生成了两个子进程。但是,父进程只希望其中的一个子进程继承内核对象句柄。调用SetHandleInformation函数来改变内核对象句柄的继承标志。函数具体参考MSDN。 
3、为对象命名
不能创建相同名字的对象,类型不同也不行。进程间共享:
进程A 有某个命名"JeffMutex" 对象 hMutexProcessA,那么进程B 创建一个同样"JeffMutex" 对象时。,
系统首先会查看是否存在一个名为“JeffMutex”的内核对象。由于确实存在这样的一个对象,所以内核接着检查对象的类型。由于试图创建一个mutex,而名为“JeffMutex”的对象也是一个mutex,所以系统接着执行一次安全检查,验证调用者是否拥有对象的完全访问权限。如果答案是肯定的,系统就会在Process B的句柄表中查找一个空白记录项,并将其初始化为指向现有的内核对象。如果对象的类型不匹配,或调用者被拒绝访问,CreateMutex就会失败(返回NULL)。
这样就实现了进程共享对象。但问题是进程B不知道自己创建的是新对象,还是用久对象。
(4)、复制对象句柄
为了跨越进程边界来共享内核对象,最后一个技术是使用DuplicateHandle函数。
这个函数获得一个进程的句柄表中的一个记录项,然后在另一个进程的句柄表中创建这个记录项的一个拷贝。
      

你可能感兴趣的:(内核对象)