目录
1 内核对象的概念
2 内核对象的使用计数
3 句柄
4 句柄表
项目工程代码中设计句柄的使用, 一时不知句柄是何物, 通过查阅自学之后, 对句柄及其使用有一个初步的了解. 分享出来, 算是抛砖引玉吧.
在阐述句柄之前, 先说明一下内核对象.
1 内核对象的概念
内核对象就是一个内存块, 有内核分配, 只能由内核访问.
内存块是一种数据结构, 其中的数据成员负责维护该对象的相应信息, 这个数据结构以及其中的数据成员只能由内核访问, 应用程序是无法访问到的, 更别说修改其中的数据成员了.
如何访问这些内核对象 (内存块) 呢?
操作系统为使用者封装了一组 API, 使用者可以通过这些 API 访问内核对象 (内存块). 比如, 创建内核对象(内存块) 时, 使用者调用 API 中的创建内核对象函数, 由内核创建一个内核对象(分配一块内存).
内核对象创建好之后, 用一个句柄来标识该内核对象 (内存块), 这个句柄作为函数值返回. 这个句柄就是个整数, 32 位机句柄就是 32bit,64 位机句柄就是 64bit, 同一进程中的任何线程都能使用这个句柄, 当需要操作内核对象(内存块) 时, 通过 API 将这个句柄传给内核, 内核就知道是对哪个内核对象 (内存块) 进行操作了.
创建内核对象, 引出句柄.
2 内核对象的使用计数
在应用程序中, 可能有多个进程, 这些进程中的一个或多个可能会访问同一个内核对象. 内核对象有使用就会有撤销, 那么什么情况下内核会撤销某个内核对象呢. 内核的使用计数就派上用场了. 使用计数是内核对象这个数据结构的数据成员, 通过使用计数就能知道该内核对象被多少个进程使用. 开始创建内核对象时, 使用计数置为 1, 之后每多一个不同的进程使用该内核对象, 使用计数就自加 1.
无论什么方式创建内核对象, 我们都需要调用 ClosseHandle 向系统表明我们已经结束使用对象. 就在 CloseHandle 函数返回前, 它会清除进程句柄表中对应的记录项 -- 这个句柄现在对我们的进程来说是无效的, 不要在试图利用它. 换句话说, 一旦调用 CloseHandle, 我们的进程就不能访问那个内核对象.
当进程关闭时, 内核自动访问该进程仍然打开的内核对象的使用计数, 该进程关联的每个内核对象的使用计数自减 1, 当使用计数减到 0 时, 内核就会撤销该内核对象.
内核对象的使用计数有些像智能指针.
3 句柄
内核对象创建好之后, 用一个句柄来标识该内核对象(内存块), 这个句柄作为函数值返回. 这个句柄就是个整数, 32 位机句柄就是 32bit,64 位机句柄就是 64bit.
在同一进程中, 一个句柄对应一个内核对象, 我们在访问内核对象 (内存块) 时, 就是通过句柄告诉内核, 我要访问哪个内核对象.
通俗的说, 句柄, 就是个编号, 操作系统对于我们来说就是个黑箱, 我们通过句柄向操作系统要东西.
4 句柄表
当一个进程被初始化时, 系统要为它分配一个句柄表. 该句柄表只用于内核对象, 不用于用户对象或 GDI 对象. 句柄表也是由内核操作. 进程表包含的元素如下所示:
索引 内核对象 内存块的指针访问屏蔽(标志位的 DWORD) 标志(标志位的 DWORD)
进程被初次初始化时, 句柄表是空的.
当进程中的线程创建内核对象时, 内核给该对象分配一块内存, 并对其初始化. 内核遍历该进程的句柄表, 找出一个空闲位置, 设置内核对象, 内存块指针, 访问掩码, 标识, 并获取该位置的索引, 作为函数值返回, 这个索引就是我们所说的句柄. 这个句柄只能有同一个进程的所有线程使用, 系统用索引来表示内核对象的信息保存在进程句柄表中的具体位置. 其他进程不能使用该进程的索引, 因为句柄表不同(每个进程有一个单独的句柄表).
所以, 句柄实际上是句柄表的索引. 可以这么理解, 指针指向一块内存空间, 那么句柄就是指向其对应的内核对象, 通过操作系统提供的 API 吧句柄传给内核, 内核就知道要操作那个内核对象.
来源: https://www.cnblogs.com/MisterXu/p/10846918.html