引子:老王果园中的果树要施肥了,需要去供销社买肥料。恰巧他又很忙,而且资金也周转不过来,于是他只好指派一位员工去供销社佘。两个小时过后,那位员工垂头丧气的跑回来告诉老王人家只人老王不认他。呵呵,够悲催的。老王只好与供销社协议,由于供销社也认识老王的儿子,最后供销社同意让他儿子过来佘。
呵呵,很现实的问题。由于老王和儿子是父子关系,人家认为以后不管老王是有钱没钱都得归小王,欠债自然也可以找小王讨。今天要讲的内核对象继承也是同样的道理,子进程可以访问父进程的的可继承对象。
下面给出代码:
父进程:
#include "stdafx.h" #include <Windows.h> #include <process.h> #include <time.h> HANDLE g_hMutext = NULL ; void GetCurTime(char* str) { time_t ct ; tm *ctm ; time(&ct) ; ctm = localtime(&ct) ; sprintf(str, "%02d:%02d:%02d", ctm->tm_hour, ctm->tm_min, ctm->tm_sec) ; } DWORD WINAPI Fun(LPVOID lp) { WaitForSingleObject(g_hMutext, INFINITE) ; printf("%d doing something now in Process Id %d\n", GetCurrentThreadId(), GetCurrentProcessId()) ; Sleep(1000 * 10) ; printf("%d has Finished in Process Id %d\n", GetCurrentThreadId(), GetCurrentProcessId()) ; char strTime[100] ; GetCurTime(strTime) ; printf("The Current time is %s\n", strTime) ; ReleaseMutex(g_hMutext) ; return 0 ; } int _tmain(int argc, _TCHAR* argv[]) { SECURITY_ATTRIBUTES saM ; saM.bInheritHandle = TRUE ; saM.lpSecurityDescriptor = NULL ; saM.nLength = sizeof(saM) ; g_hMutext = CreateMutex(&saM, FALSE, NULL) ; printf("the value of mutext handle is %d\n", g_hMutext) ; CreateThread(NULL, 0, Fun, NULL, 0, NULL) ; STARTUPINFO si = {sizeof(si)} ; SECURITY_ATTRIBUTES saProcess, saThread ; PROCESS_INFORMATION pi ; saProcess.nLength = sizeof(saProcess) ; saProcess.lpSecurityDescriptor = NULL ; saProcess.bInheritHandle = FALSE ; saThread.nLength = sizeof(saThread) ; saThread.lpSecurityDescriptor = NULL ; saThread.bInheritHandle = FALSE ; Sleep(3000) ; TCHAR szHandle[100] ; wsprintf(szHandle, _T("%d"), g_hMutext) ; CreateProcess(_T("Duplication.exe"), szHandle, &saProcess, &saThread, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi) ; while(TRUE){} return 0; }
子进程:
#include "stdafx.h" #include <Windows.h> #include <process.h> #include <time.h> HANDLE g_hMutext = NULL ; void GetCurTime(char* str) { time_t ct ; tm *ctm ; time(&ct) ; ctm = localtime(&ct) ; sprintf(str, "%02d:%02d:%02d", ctm->tm_hour, ctm->tm_min, ctm->tm_sec) ; } DWORD WINAPI Fun(LPVOID lp) { WaitForSingleObject(g_hMutext, INFINITE) ; printf("Because a thread in another process has finished, I have chance to process now\n") ; char strTime[100] ; GetCurTime(strTime) ; printf("The Current time is %s\n", strTime) ; printf("%d doing something now in Process Id %d\n", GetCurrentThreadId(), GetCurrentProcessId()) ; Sleep(1000 * 10) ; printf("%d has Finished in Process Id %d\n", GetCurrentThreadId(), GetCurrentProcessId()) ; ReleaseMutex(g_hMutext) ; return 0 ; } int _tmain(int argc, _TCHAR* argv[]) { _stscanf(argv[0], _T("%d"), &g_hMutext) ; printf("the value of mutext handle is %d", g_hMutext) ; printf("\n") ; CreateThread(NULL, 0, Fun, NULL, 0, NULL) ; while(TRUE) { } return 0; }
下面给出分析:
父进程中首先创建一个可以继承的互斥变量。为什么说他是可以继承的呢,CreateMutex的第一个参数传入了SECURITY_ATTRIBUTES结构体,该结构体的bInheritHandle字段为TRUE,表明他是可以被继承的。
然后创建调用CreateProcess创建一个子进程,该子进程的名字为Duplication.exe,同时把前面的互斥变量作为命令行参数传递给他。最重要的参数是第五个,表明是否要该子进程继承父进程。接着来看子进程的代码。子进程代码非常简单,首先将父进程传递过来的参数解析给互斥变量,然后创建一个线程。关于互斥变量请参阅前面的博文白话windows多线程同步之互斥变量。
下面是运行结果:
可以看到当父进程的线程结束时,子进程中等待互斥变量的那个线程立马开始运行,时间都是在18:42:40.说明成功实现内核对象共享。
你一定想知道他们是怎么实现共享的吧?
前面我们说过,内核对象与进程相关,但是不为进程所有,它的所有者是操作系统。进程只是拥有一个句柄表,当进程调用创建内核的函数时,操作系统会将刚才产生的内核对象的索引添加到进程的句柄表中。当父进程创建子进程时,系统会为子进程 创建一个新的、空白的进程句柄表--就像它为任何一个新的进程所做的一样。但是,如果传给CreateProcess函数的bInheritHandles参数的值是TRUE时,系统还会多做一件事:他会遍历父进程的句柄表,对他的每一个记录项进行检查。凡是包含一个有效的
”可继承的句柄“的项,都会被完整的复制到子进程的句柄表中。在子进程的句柄表中,复制项的位置与他在父进程句柄表中的位置是完全一样的。这是个非常重要的设计,他意味着:在父进程和子进程中,对一个内核对象进行标识的句柄值完全一样。
另:使子进程获取父进程句柄值的另一种方式是让父进程向其环境块中添加一个环境变量,变量的值就是要共享的句柄值。因为子进程也能继承父进程的环境变量,所以能轻松调用GetEnvironmentVariable来获得这个继承到的内核对象的句柄值。如果子进程还要生成子进程,那么这个方式就非常不错,因为环境变量是可以反复继承的。