基于Windows核心编程–句柄是什么?
什么是句柄?
句柄也就是在系统中指向某个控件或对象的唯一指针,系统可以通过这个句柄与所对应的空间或对象交互(控制它)。控件或对象与句柄的关系就好比电视机和遥控器,通过遥控器来控制电视机一样。
Windows环境中有很多对象,诸如窗口、画笔等等,可以通过API函数以不同的形式对这些对象进行操作。为此,必须以某种方法对这些对象进行标识,并把它们以参数的形式传递给函数。Windows用一个32位整数对各种对象进行标识,这就是句柄。也就是说句柄是一个对象的标识符。
句柄本身并不直接包含资源的全部信息,而是指向资源描述符的一个引用。资源描述符通常包含了资源的全部信息,如文件的位置、大小、权限等。当程序需要访问一个资源时,它会通过句柄向操作系统发出请求。操作系统根据句柄找到相应的资源描述符,并执行相应的操作。通过这种方式,句柄起到了一个桥梁的作用,将程序与底层的资源连接起来。
通俗来说:
假设你有一个快递被放进小区的智能快递柜:
- 资源 = 你的快递(对应编程里的「文件、进程、窗口、内存块」等需要系统管理的 “东西”);
- 快递柜 = 操作系统(所有资源都由系统统一管理、存放,你不能直接撬开柜子拿快递);
- 取件码(如 “88 柜 66 号”)= 句柄(系统给你的快递分配的唯一凭证,你不用知道快递在柜子里的具体物理位置,也不用管柜子的内部结构,类似的,windows 系统用句并标记系统资源,用句柄隐藏系统信息。你只需要知道有这个东西,然后去调用它就行了);
- 操作流程:
- 你用取件码在快递柜面板输入 → 系统验证取件码有效 → 自动打开对应柜子 → 你拿到快递(对应编程里:用句柄调用系统函数 → 系统通过句柄找到资源 → 执行 “读 / 写 / 关闭” 等操作);
- 取件后,取件码失效(对应编程里:关闭句柄后,句柄不能再用,否则会 “无效句柄” 错误);
- 别人的取件码打不开你的柜子(对应编程里:句柄是进程隔离的,A 进程的句柄不能操作 B 进程的资源)。
句柄和指针
句柄的声明如下:
typedefvoid*HANDLE从构造上来看,句柄是一个指针。void*是一种特殊的指针,可以与任何类型的地址进行关联。
句柄和指针都是通过一种间接的方式去操作我们的目标资源,在代码中的表现方式都是一种整形数值的表现方式(地址值本质上就是一个整形数值)。
但其实句柄和指针其实是两个截然不同的概念。
在网上看到一个指针和句柄的解释:
牧童遥指杏花村
牧童的手就是指针,杏花村的牌子为句柄,杏花村酒店为对象实例。
指针:
指针直接指向内存中某个变量、函数、对象的物理地址,得到了指针就可以直接修改该数据。指针权利太大,如果是内核对象,用指针风险极大。
句柄
句柄其实就是一个指向指针的指针,Windows是一个以虚拟内存为基础的操作系统,在这种系统环境下,Windows内存管理器经常在内存中来回移动对象,这样的话,如果某一时刻有一个指针指向一块内存,之后的某个时刻却被系统移走了,这时候你再用这个指针就会出错(系统会在内存紧张时,把暂时不用的内存块移到硬盘交换区,或在物理内存中调整位置,此时普通指针会直接失效)。而句柄恰恰可以解决这个问题,句柄是系统维护的「资源索引」,底层映射到真实指针,系统负责更新映射关系,从而让程序在内存 / 资源移动后仍能通过句柄拿到有效指针 —— 这是它和普通 C 指针最本质的区别。
和指针类似的,在程序运行过程中,若未能正确释放已不再需要的句柄,则会引发句柄泄漏,这是常见的一种资源泄漏。当泄漏发生时,会导致系统资源耗尽,严重影响系统性能。因此,良好的编码习惯中,包括确保每一个打开的资源都能被正确关闭。
学习完句柄概念,类似的,在Linux中,同样有和句柄类似的概念,也就是文件描述符,二者理念十分相似,都是通过一种间接的方式去访问资源,可以用来访问或操作底层系统资源。
示例:
HWND hWnd=CreateWindow(L"Button",L"Click Me",WS_VISIBLE|WS_CHILD,10,10,100,30,hWndParent,NULL,hInstance,NULL);if(hWnd!=NULL){// 使用句柄操作窗口对象ShowWindow(hWnd,SW_SHOW);UpdateWindow(hWnd);// ...}在上述代码中,通过CreateWindow函数创建一个按钮窗口,并将返回的句柄存储在hWnd变量中。然后,可以使用hWnd句柄来显示窗口、更新窗口等操作。
在Linux中是将所有的I/O操作抽象为文件,并使用文件描述符来引用和操作这些文件。
intfd=open("file.txt",O_RDONLY);if(fd!=-1){// 使用文件描述符读取文件内容charbuffer[1024];ssize_t bytesRead=read(fd,buffer,sizeof(buffer));// ...close(fd);}上述代码中,通过open函数打开文件file.txt,并将返回的文件描述符存储在fd变量中。然后,可以使用fd文件描述符来进行文件读取等操作。
Windows句柄类型
Windows句柄主要分为以下几种类型:
- 文件句柄:用于访问文件或设备。
- 进程句柄:用于控制另一个进程。
- 线程句柄:用于控制或监视一个线程。
- 窗口句柄:用于操作窗口和控件。
- 事件句柄:用于同步线程或进程。
- 互斥锁句柄:用于同步对共享资源的访问。
- 信号量句柄:用于控制对资源的访问数量。
- 定时器句柄:用于创建和控制定时器。
句柄工作原理
1、句柄表:每个进程在创建时,会被分配一个句柄表。当程序请求系统资源时,操作系统在句柄表中生成一个唯一标识,并返回给程序,这个标识即为句柄。
2、资源访问:通过句柄,操作系统能够在资源被多重锁定的情况下,确保对该资源的有效操作。这是通过内部的句柄验证和权限检查机制实现的。在多线程环境下尤其重要,它能防止资源竞态条件的发生。
3、异常处理:使用句柄可以简化错误处理,通过检查句柄的有效性,程序可以对错误进行捕捉与处理,诸如文件未找到、套接字连接失败等错误状态均可通过句柄返回的错误码来判断。