作者:yurunsun@gmail.com 新浪微博@孙雨润 新浪博客 CSDN博客日期:2012年11月3日
1. 何为内核对象
1.1 定义
内核对象是一个内存中的数据结构,只能由OS内核分配访问。访问内核对象的API会返回进程相关的Handle,无法直接跨进程共享。内核对象使用Count Reference,Life Cycle可能长于创建它的进程。
【Note】使用Sysinternals的WinObj可以查看所有内核对象类型的列表。
1.2 安全性
创建内核对象的API都有SECURITY_ATTRIBUTES
做参数,如果不需要加访问限制则传NULL。如果想访问现有内核对象,需要指定即将执行的操作:
HANDLE hFileMap = OpenFileMapping(FILE_MAP_READ, FALSE, _T("TestFileMap"));
如果访问被拒绝会返回NULL。
2. 进程内核对象句柄表
进程初始化时系统为其分配一个Handle table,仅供内核对象使用而非用户对象或GDI对象。
2.1 创建
下面是一些用于常见内核对象的API:
HANDLE CreateThread(...);
HANDLE CreateFile(...);
HANDLE CreateFileMapping(...);
HANDLE CreateSemaphore(...);
【Note】CreateFile返回值与INVALID_HANDLE_VALUE比较,其余与NULL比较。
2.2关闭
CloseHandle(HANDLE hObject);
3. 跨进程边界共享内核对象
下面情况需要共享内核对象:
- 利用FileMapping,在同一个OS上不同Process共享数据
- 借助Mail Slot/Pipe,在网络中不同PC上运行的进程互相发送数据
- Semaphore、Event、Mutex允许不同Process中的Thread同步执行
3.1 方法一:对象Handle继承
只有Process之间有父子关系时使用这种方式,使子进程能访问父进程的内核对象。
-
使用上边提到的
SECURITY_ATTRIBUTES
:SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; HANDLE hMutex = CreateMutex(&sa, FALSE, NULL);
-
调用
CreateProcess
创建子进程,bInheritHandles传TRUE
- OS遍历父进程Handle table,将Inheritable的内核对象copy到子进程的Handle table,子进程的Handle table中复制项位置与父进程中位置完全一样,保证Handle相同
- OS递增引用计数
- 正在运行的子进程不会继承父进程后来创建的新内核对象
- 通常使用命令行参数、IPC、环境变量几个方法使子进程知道继承的Handles
3.2 改变Handle的Flag
父进程想控制哪些子进程能继承内核对象Handle:
BOOL SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags);
SetHandleInformation(hObj, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); // Open
SetHandleInformation(hObj, HANDLE_FLAG_INHERIT, 0); // Close
SetHandleInformation(hObj, HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE); // 禁止close句柄
3.3 方法二:为内核对象命名
许多内核对象创建时可以使用pszName
命名,pszName
为NULL
时为匿名对象。【Note】Create*函数不知道刚刚是新建了一个内核对象,还是打开了一个现有的同名内核对象,因此最好使用Open*函数测试。
3.4 方法三:Copy对象Handle
进程S能访问一个内核对象,希望T也能访问也能访问:
HANDLE hObjInProcessS = CreateMutex(NULL, FALSE, NULL);
HANDLE hProcessT = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessIdT);
HANDLE hObjInProcessT;
DuplicateHandle(GetCurrentProcess(), hObjInProcessS, hProcessT, &hObjInProcessT, 0, FALSE, DUPLICATE_SAME_ACCESS);
CloseHandle(hProcessT);