3.1什么是内核对象
作为一个Windows软件开发人员,你经常需要创建,打开和操作各种内核对象.系统要创建和操作若干类型的内核对象,比如存取符号对象,事件对象,文件对象,文件映射对象,I/O完成端口对象,作业对象,信箱对象,互斥对象,管道对象,进程对象,信标对象,线程对象和等待计数器对象等.这些对象都是通过调用函数来创建的.
3.1.1内核对象的使用计数
内核对象是由内核所拥有的,而不是由进程所拥有的.内核对象知道有多少进程正在使用某个内核对象,因为每个对象包含一个使用计数.对象被创建的时候计数被置为1.然而当另外一个进程访问一个现有的内核对象的时候,使用计数器就递增1.当进程终止运行的时候,内核对象自动确定该进程仍然打开的所有内核对象的使用计数.如果内核对象使用的使用计数降为0,内核对象就撤消该对象.
3.1.2安全性
内核对象能够得到安全描述符的保护. 安全描述符用于描述谁创建了该对象.谁能够访问或使用对象,谁无权访问该对象.用于创建一个内核对象的函数几乎都有一个指向SECURITY_ATTRIBUTES结构的指针作为其参数,比如:
HANDLE CreateFileMapping(
HANDLE hFile,
PSECURITY_ATTRIBUTE psa,
DWORD flProtect,
DWORD dwMaximumSizeHign,
DWORD dwMaximimSizeLow,
PCTSTR pszName);
其中SECURITY_ATTRIBUTES结构定义如下:
typedef struct _SECURITY_ATTRIBUTE{
DWORD nLength,
LPVOID lpSecurityDescription,
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES;
区分用户对象和内核对象最简单的方式就是,一般使用内核对象都有安全属性,而用户对象都没有.比如CreateIcon这个函数创建一个GDI对象,它就不是一个内核对象而是一个用户对象.
3.2进程的内核对象句柄表
进程的句柄表都包含一个指向内核对象的指针,一个访问屏蔽和一些标志. 3.2.1创建内核对象
创建内核对象需要注意的一点就是当调用CreateFile函数时,才能将该值与INVALID_HANDLE_VALUE进行比较.下面的代码就不正确:
HANDLE hMutex=CreateMutex(...);
if(hMutex==INVALID_HANDLE_VALUE)
同样下面的代码也不正确:
HANDLE hFile=CreateFile(...);
if(hFile==NULL)
3.2.2关闭内核对象
无论怎样创建内核对象,都要向系统指明将通过调用CloseHandle来结束对该对象的操作.
BOOL CloseHanle(HANDLE hobj);
不要试图使用一个已经关闭的句柄对象,假如忘记调用CloseHandle函数,会造成内存泄漏吗?答案是可能的,但是也不一定.
3.3跨越进程边界共享内核对象
3.3.1对象句柄的继承性
只有当进程具有父子关系时,才能使用对象句柄的继承性.虽然内核对象的句柄具有继承性,但是内核对象本省不具有继承性.若要创建一个具有继承性的句柄,父进程必须指定一个SECURITY_ATTRIBUTES结构并对它进行初始化,然后把该结构传递给Create函数.下面的代码创建了一个具有继承性的互斥对象:
SECURITY_ATTRIBUTE sa;
sa.nLength=sizeof(sa);
sa.lpSecurityDescriptor=NULL;
sa.bInheritHandle=TRUE; //make the returned handle inheritable
HANDLE hMutex=CreateMutex(&sa,FLASE,NULL);
要使具有继承性的句柄生成一个子进程.要使用CreateProcess函数来完成.
3.3.2改变句柄的标志
若要改变内核对象句柄的继承标志,可以调用SetHandleInformation函数:
BOOL SetHandleInformation(
HANDLE hObject,
DWORD dwMask,
DWORD dwFlags);
hObject就是一个有效的句柄.第二个参数dwMask告诉函数想要改变那几个标志,第三个参数就是标志将要设置成什么值.可以用该函数来设置句柄的继承标志,等等.
3.3.3命名对象
共享跨越进程边界的内核对象的第二种方法就是给对象命名.许多内核对象都是可以命名的.但是这存在一个问题,Microsoft没有提供位内核对象赋予名字的知道原则.假如你创建一个"JeffObj"的对象,那么不能保证系统是不存在一个名字位"JeffObj"的对象.