多进程共享内核对象

内核对象是什么?

首先,内核对象是什么?引用《Windows核心编程》原文:

“每个内核对象都只是一个内存块,它由操作系统内核(Ring0)分配,并只能由操作系统内核访问。”

内核对象存在于进程虚拟地址空间的高地址(32位:0x80000000-0xFFFFFFFF),它由三个部分组成:对象头(_OBJECT_HEADER)、对象体(_EPROCESS/_ETHREAD)和附加信息。
(书中提到的引用计数和安全描述符存在于对象头中)

由于内核对象只能由操作系统内核访问,应用程序(Ring3)不可直接访问,因此,操作系统提供了一组API函数用于对内核对象的操作。内核对象被创建时,函数返回一个句柄,用于标识所创建的对象,这些API函数可通过句柄对对象进行访问。句柄值和进程是相关的(每个进程各自维护一个句柄表),所以,依靠进程间通信传递句柄值并使用,便可能会发生错误(在不同进程同一句柄值可能标识不同的内核对象)。

多进程共享内核对象

多进程共享内核对象有三种方式:内核对象句柄继承、命名对象和复制对象句柄。

父子进程句柄继承

内核对象句柄继承的关键在于两个进程必须具有父子关系,即一个进程生成另外一个进程。子进程成功继承并能使用继承的内核对象需满足两点条件:一是父进程需设置该内核对象为可继承对象;二是父进程需将句柄值通过某种进程间通信的方式(命令行参数等)传递给子进程。

需要注意的是:
1.父进程句柄表中“可继承句柄”会复制到子进程句柄表相应位置,也就是说它们的句柄值是相同的。这也是父进程和子进程共享内核对象的前提。
2.内核对象句柄的继承只发生在子进程生成时,之后父进程再生成的“可继承对象”不再继承给子进程。
3.子进程需要通过某种方式获得句柄值,否则继承无效(无法对内核对象进行操作)。

测试代码:
创建一个可继承的事件内核对象,在创建子进程的时候选择继承,并将事件对象的句柄值以命令行参数(argv)传入子进程。子进程得到句柄值,并调用SetEvent函数。事件授信,父进程弹框提示成功。

代码关键点:
1.允许内核对象继承
2.通过某种方式传给子进程句柄值

//父进程代码
#include<tchar.h>
#include<windows.h>

VOID _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
	TCHAR BufferData[20] = { 0 };
	STARTUPINFO StartupInfo = { 0 };
	StartupInfo.cb = sizeof(STARTUPINFO);
	PROCESS_INFORMATION ProcessInfo = { 0 };

	//创建安全描述符结构体,并设置句柄可继承(第三参数)
	SECURITY_ATTRIBUTES SecurityAttributes{ sizeof(SECURITY_ATTRIBUTES),NULL,TRUE };

	//创建一个可继承的事件内核对象
	HANDLE EventHandle = CreateEvent(&SecurityAttributes, TRUE, FALSE, NULL);		
	if (EventHandle == NULL)
	{
		OutputDebugString(_T("事件创建失败\r\n"));
		goto Exit;
	}
	else
	{
		_tprintf(_T("EventHandle:%d\r\n"), EventHandle);

		//将句柄值格式化成字符串
		_stprintf_s(BufferData, _T("%d"), EventHandle);			
		_tprintf(_T("BufferData:%s\r\n"), BufferData);

		//创建子进程
		BOOL IsOk = CreateProcess(_T("SubProcess.exe"),
			(LPTSTR)BufferData, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &StartupInfo, &ProcessInfo);
		if (IsOk == FALSE)
		{
			OutputDebugString(_T("创建子进程失败\r\n"));
		}

		//解除父进程对子进程资源的占有					//为何?
		if (ProcessInfo.hProcess != NULL || ProcessInfo.hThread != NULL)
		{
			CloseHandle(ProcessInfo.hProcess);
			CloseHandle(ProcessInfo.hThread);
		}
		//等待事件授信
		WaitForSingleObject(EventHandle, INFINITE);
		//子进程设置这个事件则授信,并弹出MessageBox
		MessageBox(NULL, _T("子进程触发事件,继承成功"), _T("Success"), 1);

		_tprintf(_T("Input AnyKey To Exit\r\n"));
		_gettchar();
	}
Exit:
	if (EventHandle != NULL)
	{
		CloseHandle(EventHandle);
	}
}

//子进程代码
#include<tchar.h>
#include<windows.h>

VOID _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
	HANDLE EventHandle = 0;

	//命令行参数是单字的,将字符串格式化为句柄值(数字)
	_stscanf_s((TCHAR*)argv[0], _T("%d"), &EventHandle);
	_tprintf(_T("EventHandle:%d\r\n"), EventHandle);

	__try
	{
		if (EventHandle != NULL)
		{
			SetEvent(EventHandle);
		}

		//停顿5s,查看一下传进来的句柄值
		Sleep(5000);

		if (EventHandle != NULL)
		{
			CloseHandle(EventHandle);
		}
		ExitProcess(0);
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		int LastError = GetExceptionCode();
	}
}

多进程共享内核对象——命名对象

测试代码:
进程1创建命名事件对象,进程2使用相同名称创建事件对象,并调用SetEvent函数。事件授信,进程1弹窗提示成功。

//进程1
#include<windows.h>
#include<tchar.h>

VOID _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{

	HANDLE EventHandle = CreateEvent(NULL, TRUE, FALSE, _T("666"));
	if (EventHandle == NULL)
	{
		OutputDebugString(_T("事件创建失败\r\n"));
		goto Exit;
	}

	WaitForSingleObject(EventHandle, INFINITE);
	MessageBox(NULL, _T("子进程触发事件"), _T("Success"), 1);

	_tprintf(_T("Input AnyKey To Exit."));
	_gettchar();
Exit:
	if (EventHandle != NULL)
	{
		CloseHandle(EventHandle);
	}
}

//进程2
#include<windows.h>
#include<tchar.h>

VOID _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{

	HANDLE EventHandle = CreateEvent(NULL, TRUE, FALSE, _T("666"));
	if (EventHandle != NULL)
	{
		SetEvent(EventHandle);
	}

	//停顿5S,查看一下传进来的句柄值
	Sleep(5000);

	if (EventHandle != NULL)
	{
		CloseHandle(EventHandle);
	}
	ExitProcess(0);
}

补充

一、句柄表

一个进程初始化时会生成一个专供内核对象使用的句柄表,句柄表本质是一个结构体数组。结构体(记录项)包含指向内核对象头(_OBJECT_HEADER)的指针、访问掩码和句柄的属性(继承属性和禁止关闭)等。

创建内核对象时进程会为其在句柄表中分配一个记录项,函数返回内核对象的句柄(句柄表索引的四倍)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值