内核对象(3)

来源:互联网 发布:扑克牌喝酒游戏知乎 编辑:程序博客网 时间:2024/06/14 05:06

         1、内核对象包括:时间对象,文件对象,I/O完成端口对象。作业对象,进程对象,线程对象,互斥对象,信号量对象等,具体的可以通过网址:http://www.microsoft.com/technet/sysinternals/utilities/winobj.mspx获得内核对象列表查看。

    内核对象都是调用不同名称的函数来完成的,比如:调用Createthread函数,系统将创建一个线程。每个内核对象都只是一块内存块(数据结构),它由操作系统内核分配,且只能操作系统内核访问。这个内存块是一个数据结构,其成员维护着与对象相关的信息。(其中少数成员是所有对象都有的,如安全描述和使用计数等)对于,进程对象,其独有进程ID,一个基本优先级和一个退出代码;而一个文件对象独有一个字节偏移量、一个共享模式和打开模式。

    当调用了一个创建内核对象的函数后,会返回一个句柄(handle),它标识了所创建的这个对象。在32bit系统中其为32位值,64bit系统下,则为64位值。这些句柄是与进程相关联的(下面会介绍如何相关联)。

内核对象有使用计数机制,初次创建时引用计数是1,另一个进程获得现有内核对象的访问权后,使用计数会增加,使用该内核对象的进程终止时,会递减。当使用计数到0时,被销毁。

        

      2、一个进程在初始化时,系统会分配一个句柄表。(句柄表提供内核对象的使用,不包括用户对象和GDI对象,比如图片资源对象等不属于内核对象)

    句柄表:是一个数据结构数组,每个元素都是一个指针(其指向一个内核对象),一个访问掩码和一些标志。(可以将句柄理解为此表的索引

进程一开始,句柄表为空。当在其主线程中,创建一个内核对象后,内核会创建相应的信息,并且放在一块内存块中(此时表不为空了)。返回的句柄,可以由此进程中所有的线程调用。

    调用一个函数时,如WaitForSingleObject函数。其内部会查找进程的句柄表,获得内核对象的指针,然后用一种恰当的方式处理内核对象的数据结构。传入无效的handle,会失败。可以调用GetLastError查看。

    由于handle是作为表的索引的,所以,handle与进程相关联。如果在其他进程中引用,那么其会索引到其他进程的表,会发生错误。

    调用CloseHandle函数关闭内核对象。其内部,会先检查主调进程的句柄表,验证这个进程“确实有权访问这个对象”。如果句柄有效,则“使用计数”减1。如果计数成0,内核对象将被销毁。在函数返回前,会清理进程句柄表中的对应项-------即这个句柄现在是无效的,不要再访问它了。无论内核对象当前是否销毁,这个清除都会发生。

    也就是,一点调用了CloseHandle,我们的进程就不能访问那个内核对象了;但是,如果这个内核对象的“计数“不为0,则对象不会被销毁,这是正常的。因为,它表明只是当前线程或进程对此内核对象不再感兴趣,不会再使用,但是可能还有其他的线程或进程在使用该内核对象。当全部的进程和线程都不再感兴趣时,此内核对象“使用计数”被递减为0,被销毁。

    另外,进程终止时,所有的内核对象、资源以及内存块等,都会被释放掉。

      

      3、跨进程共享内核对象

    1、对象句柄继承:在进程之间有父子关系时可以使用。步骤如下:(1)父进程创建内核对象时,指定此内核对象的句柄可被继承的。

SECURITY_ATTRIBUTES sa;sa.nLength =sizeof(sa);sa.lpSecurityDescriptor =NULL;sa.bInheritHandle =TRUE;//指定为句柄可继承
HANDLE hMutex=CreateMutex(&sa,FALSE,NULL);
这样变指定了创建的内核对象的句柄是可被继承的了。

                                                                                                             (2)父进程创建子进程。调用CreateProcess函数,并将其中的bInHeritHandles参数设置为TRUE,表明我们希望子进程继承父进程的“可继承句柄”。

    2、为对象命名:当在A进程中调用createmutex创建一个名为“Jeff”的内核对象后,在B进程(非父子关系也行)中调用createmutex创建“Jeff”时,会先查看内核是否有同名对象,如果存在,则检查类型,如果类型也相同,(则再执行安全检查,看其是否有访问权限),都通过的情况下,系统会在B进程的句柄表中找一个空白记录项,将其与现有内核对象绑定。如果类型不匹配,或调用者被拒绝访问,则createmutex函数调用失败(返回NULL)。如果没有同名对象,则创建新的内核对象。

也可以调用OpenMutex函数来共享。如果没有同名,返回NULL。如果同名,类型不同,返回NULL。如果同名,类型同,且允许访问(通过dwDesiredAccess指定),则绑定且返回。

open*和create*这两个系列函数唯一的不同是,如果同名对象不存在,create会创建,open会返回NULL。

    3、赋值对象句柄:调用DuplicateHandle函数。简单的说,就是获得当前调用进程句柄表的记录项,然后在另一个进程的表中创建这个记录项的一个副本。