在很多时候,不同进程中运行的线程需要共享内核对象,比如一些同步操作。
三种不同机制来允许进程共享内核对象:
① 使用对象句柄继承;
② 为对象命名;
③ 复制对象句柄。
(1)
只有进程之间存在父子关系的时候才可以使用对象句柄继承。首先要创建一个可继承的句柄,父进程必须分配并初始化一个SECURITY_ATTRIBUTES结构,并将该结构的地址传给具体的Create函数。以下代码创建了一个互斥量对象,并返回其可继承的句柄:
SECURITY_ATTRIBUTES
sa.nLength
sa.lpSecurityDescriptor
sa.bInheritHandle
HANDLE
接下来谈谈进程的句柄表记录项中保存的标志。句柄表中的每个记录项都有一个指明句柄是否可以继承的标志位。如果在创建内核对象时,将NULL作为PSECURITY_ATTRIBUTES参数传入,则返回的句柄是不可继承的,这个标志位为0.将bInheritHandle成员设为TRUE。则这个标志位为1。
索引 | 指向对象内存块的指针 | 访问掩码(包含标志位的一个DWORD) | 标志 |
1 | 0xF0000000 | 0x??? | 0x00000000 |
2 | 0x00000000 | (不可用) | (不可用) |
3 | 0xF0000010 | 0x??? | 0x00000001 |
为了使用对象句柄继承,下一步是由父进程生成子进程。这是通过CreateProcess函数来完成的。
主要注意bInheritHandles参数。通常情况下,在生成一个进程时,我们要将该参数设为FALSE,这个值向系统表明,我们不希望子进程继承父进程句柄表中的“可继承句柄”;相反,传入TRUE时,将继承所有可继承句柄;传入TRUE时,操作系统创建了子进程,但不允许子进程立即运行,系统会为子进程创建一张新的、空白的进程句柄表,将父进程可继承句柄全部复制到子进程的句柄表中,
注意它们在父进程和子进程中的位置是一样的,这意味着,在父进程和子进程中,标识内核对象的句柄值是一致的。同时,系统还会递增内核对象的使用计数,为了销毁内核对象,父进程和子进程要么都调用CloseHandle(),要么都终止运行。
(2)
可以对内核对象命名,这种方式的一个优势是进程间不一定存在父子关系。
(3)
BOOL
HANDLE
HANDLE
HANDLE
LPHANDLE
DWORD
BOOL
DWORD
简单的说,这个函数获得一个进程的句柄表中的一个记录项,然后在另一个进程的句柄表中创建该记录项的一个副本。
要注意的是,hSourceHandle是相对于hSourceProcessHandle的,lpTargetHandle是相对于hTargetProcessHandle的。
dwOptions
如果指定DUPLICATE_SAME_ACCESS,将向DuplicateHandle函数表明我们希望目标句柄拥有与源进程的句柄一样的访问掩码。使用这个标志后,DuplicateHandle函数会忽略它的dwDesiredAccess参数;
如果指定DUPLICATE_CLOSE_SOURCE标志,将会关闭源进程中的句柄。利用这个标志,一个进程可以轻松地将一个内核对象传给另一个进程。如果使用了这个标志,内核对象的使用计数不会受到影响。
应避免在源进程中关闭复制后的目标句柄,因为目标句柄是相对于目标进程来说的。
使用DuplicateHandle函数(来复制内核对象句柄)所遇到的问题和继承(内核对象句柄)时同样:目标进程不知道它现在能访问一个新的内核对象。所以进程C必须以某种方式来通知进程T,告诉它可以访问一个内核对象。显然,使用命令行参数或者更改进程T的环境变量是行不通的,因为进程已经启动运行了。我们必须使用窗口消息或者其它进程间通信(IPC)机制。