您的位置:首页 > 编程语言 > C语言/C++

Windows Via C/C++ Part Ⅰ Chapter3: 内核对象(1)

2009-08-20 00:14 381 查看
概要
在本章我们将通过接触内核对象及其句柄开始了解Micorosoft Windows application programming interface。本章并不会深入讲解关于特定类型内核对象的细节,而将重点放在所有内核对象通用的属性上。
我原本可以开始直接讨论某一内核对象的具体细节,然而熟练掌握内核对象的基础知识对于windows软件开发者来说至关重要。操作系统和应用程序使用内核对象管理大量的资源,譬如进程、线程、文件等。本章阐述的概念将贯穿后面的章节,由于这些知识只有通过实践才能真正的理解和掌握,因此你可能在阅读本书其它章节时还需要不时参阅本章内容。

1 什么是内核对象
Windows软件开发人员经常需要创建、打开并操作内核对象——即使你没有意识到你正在处理它。操作系统维护着若干种内核对象,比如访问令牌对象(access token objects)、事件对象(event object)、文件对象(file object)、文件映射对象(file-mapping object)、I/O完成端口对象(I/O completion port objects)、作业对象(job object)、邮件槽对象(mailslot object、互斥对象(mutex object)、管道对象(pipe object)、进程对象(process object)、信号量对象(semaphore object)、线程对象(thread object)等等。可以使用微软提供的WinObj工具(http://www.microsoft.com/technet/sysinternals/utilities/winobj.mspx)查看内核对象列表。
这些内核对象通过调用不同的函数创建,但是函数名有时和它所创建的内核对象名并无直接关系,比如CreateFileMapping函数创建的文件映射对象在内核级别其实对应着一个Section对象。每个内核对象代表一块只能由内核访问的内存区域,其中保存着内核对象的属性。有些属性是所有内核对象通用的,比如安全描述符(security descriptor)、引用计数(usage count)等等,而另一些则是每种内核对象特有的,比如一个进程对象拥有进程ID、基本优先级、退出码等,而一个文件对象的属性则包括字节偏移量、共享模式和打开模式等。
内核对象的数据结构只能由内核访问,应用程序无法在内存中定位这些结构也不能直接改变其内容。这一限制保证了内核对象的状态是一致的,也保证了无论微软对内核对象实现细节做怎么样的修改都不会破坏已有的程序。
既然不能直接更改内核对象的结构,我们的程序如何来操作这些对象呢?答案是微软提供了一组定义良好的函数用来操作内核对象。你可以通过某个函数创建内核对象并返回其句柄,句柄用来标识与其相关联的内核对象,在32位的进程中句柄是一个32位的值,在64位的进程中则是64位的。开发者通过将句柄传递给Widnows函数来操纵相应的内核对象。句柄被设计成进程间独立的以增强操作系统的健壮性。如果你想将某个句柄传递给另一进程中的线程使用,这将会引发一些问题,在“进程间共享内核对象”一节中我们将对此做详细讨论。

2 引用计数(Usage Count)
内核对象的拥有者是内核,而不是任何进程。假如某一进程在运行时创建了一个内核对象,在其结束时内核对象也无需销毁,这些对象将一直生存到最后一个使用它的进程终止。内核知道当前有多少进程正在使用某一内核对象——这是由每一个内核对象的引用计数得知的。当对象被创建时,其引用计数被设为1,每当另外的进程访问该对象时,其引用计数加1。当某一进程终止时,内核会自动将所有与该进程关联的内核对象的引用计数减1,如果某个对象的引用计数为变为0,内核将销毁它。这保证了没有进程关联的内核对象不会遗留在系统中。

3 安全性
安全描述符(Security Descriptor)用来保护内核对象,它描述了对象的所有者及其访问权限。安全描述符通常用于服务端应用开发中,不过Windows Vista下的客户端应用也越来越多的开始使用安全描述符和私有命名空间以提高安全性。
几乎所有用来创建内核对象的函数都有一个指向SECURITY_ATTRIBUTES结构的指针作为参数,比如:
]HANDLE CreateFileMapping(
HANDLE hFile,
PSECURITY_ATTRIBUTES psa,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
PCTSTR pszName);


大多数程序只为该参数传递NULL,表示对象将使用基于当前进程安全标志的默认安全性。当然你也可以自己创建SECURITY_ATTRIBUTES类型的变量,对其进行初始化,然后把它的地址传给目标函数作为参数。SECURITY_ATTRIBUTES结构的定义如下:
]typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES;

在SECURITY_ATTRIBUTE中,只有一个成员和安全性有关:lpSecurityDescriptor,如果你想对某一内核对象设置严格的访问权限,可以通用类似下面的方式完成:
]SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);         // Used for versioning
sa.lpSecurityDescriptor = pSD;   // Address of an initialized SD
sa.bInheritHandle = FALSE;       // Discussed later
HANDLE hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, 1024, TEXT("MyFileMapping"));


成员bIneritHandle和安全性没有丝毫联系,我们将在“使用对象句柄继承”一节中讨论它。
如果要获得某个已存在的内核对象的访问权限,你必须在函数中指定你要执行的操作类型,比如当打开一个已存在的文件映射对象以读取数据时,应该这样调用OpenFileMapping:
]HANDLE hFileMapping = OpenFileMapping(FILE_MAP_READ, FALSE, TEXT("MyFileMapping"));

向OpenFileMapping传递参数FILE_MAP_READ表示调用者打算从文件映射区中读取数据。OpenFileMapping在执行时会先检查安全权限,如果发现当前用户无权访问该对象或无权在该对象上执行指定操作, 函数将返回NULL并将错误码设置为5(ERROR_ACCESS_DENIED),否则函数返回关联该对象的有效句柄。不要忘记在后续使用该句柄的函数调用中,你所执行的操作应该和打开对象时指定的操作(本例中的FILE_MAP_READ)相一致,否则同样会发生ERROR_ACCESS_DENIED错误。由于大多数应用程序不会用到安全性检查,因此我们将不再深入该话题。
尽管很多应用程序不需要关注安全性,但是Windows函数仍然要求调用者传递适合的安全访问信息,就像上面所讨论的。某些为之前的Windows版本设计的应用程序在开发时由于没有考虑到这个因素而无法在Vista下正常运行。比如某个应用程序需要读取注册表的某个子键,为些应该调用RegOpenKeyEx并传递参数KEY_QUERY_VALUE以获得相应权限。然而许多为Win2000之前的操作系统设计的程序并没有考虑安全因素,有些开发人员调用RegOpenKeyEx并传递了参数KEY_ALL_ACESS,这样的用法确实比较简单,然而像HKLM这样的子键可能不允许非管理组的用户写入,虽然这在Win2000之前的系统上面没有什么问题,但在Vista下非管理员以KEY_ALL_ACESS权限调用RegOpenKeyEx会失败,这样某些旧的程序可能导致无法预测的运行结果。
忽略合适的安全访问信息是开发人员所犯的最大错误之一,使用正确的权限标志将使应用程序更加方便的在各个Windows版本上移植。但是你也应该注意到每个新的Windows版本都会伴随着新的权限值发布。比如在Vista下,开发者应该注意User Account Control(UAC)这一新的特色。UAC默认强制应用程序在被限制的上下文中执行以提高安全性,即使当前用户属于管理组。在第4章“进程”中我们将继续讨论UAC机制。
除了使用内核对象,应用程序也许会用到其它类型的对象,如菜单、窗口、鼠标光标、画刷、字体等。这些对象是用户对象(User Objects)或是图形接口对象(Graphical Device Interface Objects)。如果你是首次接触Windows编程,你可能会被各个对象所属的范畴搞晕,比如图标是用户对象还是内核对象?窗口呢?最简单的辨别方法是去观察创建该对象的Windows函数,几乎所有创建内核对象的Windows函数都有一个指向SECURITY_ATTRIBUTES结构的指针作为其参数,如:
]HICON CreateIcon(
HINSTANCE hinst,
int nWidth,
int nHeight,
BYTE cPlanes,
BYTE cBitsPixel,
CONST BYTE *pbANDbits,
CONST BYTE *pbXORbits);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: