内核对象是Windows操作系统内核分配和访问的对象,每个内核对象对应于一个内存块,该内存块只能由内核分配,访问和释放。该内存块是一个数据结构,维护着与对象相关的信息。少数成员是所有内核对象所共有的,如:安全描述符和使用计数,其他多数成员则是每种内核对象所特有的。
内核对象有很多种,如:访问令牌,事件,文件,文件映射,作业,互斥体,管道,进程,线程,信号量,可等待计时器以及线程池工厂等。可使用一些工具来观察,如:WinObj,Process Explorer等。
内核对象在应用程序中只能通过Windows的一组函数Create*/Open*/CloseHandle以及相应的内核对象句柄(*Handle)来访问,内核对象句柄(32位Windows进程中句柄为32位,64位进程中,句柄也变成64位)是一个不透明值,其具体意义也可能与Windows不同版本的具体实现相关,不过能够确定的是:
内核对象的所有这是内核,而非进程,而且进程操作内核对象的唯一手段就是用句柄作为参数来调用相应的Windows API(Create*/Open*/CloseHandle)。Windows操作系统可由此来实现对内核对象的使用计数,就是说同一个内核对象,Windows操作系统知道有多少进程在引用它。只有当引用计数变为0的时候,内核对象才被销毁,因此,一个内核对象的生命周期有可能比创建它的进程生命周期要长。
内核对象都使用一个安全描述符来保护,安全描述符定义了谁拥有对象,哪些组和用户被允许访问和使用此对象,哪些被拒绝。该安全信息用参数SECURITY_ATTRIBUTES来指定,如果传NULL给这个参数,则表示使用默认的安全性(具体包括哪些安全信息,取决于当前进程的安全令牌security token)。
除了内核对象,进程还可能使用一些其他对象,如菜单,窗口,鼠标,笔刷和字体,这些属于用户对象或GDI对象,而非内核对象。一个简单的方法来判断是否是内核对象,就看其Create*方法有没有指定安全信息的参数。
进程的内核对象句柄都包含了进程句柄表的索引值,而进程的句柄表是在进程初始化的时候系统为其分配的;实际上将句柄值除以4(或者右移2位,以忽略Windows内部使用的最后两位)就能得到实际的句柄表索引值。一个可能的句柄表结构如下图:
每个句柄包含一个索引值,能够只想该表中的一条记录,而该句柄表中每一条记录都是一个数据结构,这个数据结构包含了:
调用Create*函数来创建内核对象,如下面的列表所示,创建失败时,有的函数会返回NULL(这就是为神马第一个有效句柄值为4的原因),有的则会返回INVALID_HANDLE_VALUE(这个值是-1);但是GetLastError()都会返回ERROR_INVALID_HANDLE。
CreateProcess/CreateThread/CreateFile/CreateFileMapping/CreateMutex/CreateSemaphore
关闭内核对象,无论是哪种类型,如何创建的,都使用CloseHandle;如果关闭失败,CloseHandle将返回FALSE。
调用CloseHandle后,将导致进程句柄表中相应的记录被清空,并且将相应内核对象引用计数减1,如果计数变成0则销毁该内核对象。
如果进程忘记调用CloseHandle,那就要等到进程退出时,系统才会为进程关闭所有使用到的内对象句柄。事实上,进程终止时,系统会确保所有的资源都得到释放,包括内核对象,资源,GDI对象以及内存块。(使用Windows任务管理器或Process Explorer可以验证这一点)。
由于内核对象是保存在进程内核地址空间中的,所以可以跨进程共享内核对象。系统中所有的进程都共享内核地址空间:对32位系统,这是0x80000000~0xFFFFFFFF之间的内存,对64位系统,这是0x00000400 00000000~0xFFFFFFFF FFFFFFFF之间的空间。
共享内核对象的三种方式:
DWORD currentProcessId=GetCurrentProcessId();
HANDLE hMutexInSource=CreateMutex(NULL, FALSE, _T("MyTestMutex"));
STARTUPINFO si = { sizeof(si) };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = TRUE;
PROCESS_INFORMATION pi={};
HANDLE hMutextInTarget=NULL;
TCHAR cmdline[] =TEXT("c://program files//internet explorer//iexplore.exe http://www.baidu.com/");
if(CreateProcess(NULL, cmdline, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)){
HANDLE hSourceProcess = GetCurrentProcess();
tcout<<"before duplicate handle"<<endl;
//display_process_handles(pi.dwProcessId);
if(!DuplicateHandle(hSourceProcess,hMutexInSource,pi.hProcess,&hMutextInTarget,0,FALSE,DUPLICATE_SAME_ACCESS)){
display_last_error();
}
else{
tcout<<"after duplicate handle"<<endl;
//display_process_handles(pi.dwProcessId);
}
setlocale(LC_ALL, "chs");
_tprintf(_T(" 新进程的进程ID号:%d\n"), pi.dwProcessId);
_tprintf(_T(" 新进程的主线程ID号:%d\r\n"), pi.dwThreadId);
CloseHandle(pi.hProcess);
}
else{
display_last_error();
}
CloseHandle(hMutexInSource);