什么是句柄
句柄就是一个对象的标识符,只要获得对象的句柄,我们就可以对对象进行任意的操作,包括窗口,按钮,图标,输出设备,控件或者文件等;
句柄是一种特殊的智能指针,用一个唯一的整数值标识一个对象(即编号),并不指向实际的内核对象,而是内核对象的虚拟地址;
只有Windows中才有句柄,Windows中的句柄是指针的指针,因为windows中对象的经常会在内存中移动,所以地址值经常会变,所以就对外提供一个指针的指针即句柄给用户,句柄的地址是不会变的。
Linux中是没有文件句柄的,只有文件描述符,只是大家习惯把它说成句柄,Linux中, 每当进程打开一个文件时,系统就为其分配一个唯一对应的整型文件描述符,用来标识这个文件;
Windows之所以要设立句柄,根本上源于内存管理机制的问题—虚拟地址,简而言之数据的地址需要变动,变动以后就需要有人来记录管理变动,(就好像户籍管理一样),因此系统用句柄来记载数据地址的变更。
Windows系统中有许多内核对象(这里的对象不完全等价于"面向对象程序设计"一词中的"对象",虽然实质上还真差不多),比如打开的文件,创建的线程,程序的窗口,等等。这些重要的对象肯定不是4个字节或者8个字节足以完全描述的,他们拥有大量的属性。为了保存这样一个"对象"的状态,往往需要上百甚至上千字节的内存空间,那么怎么在程序间或程序内部的子过程(函数)之间传递这些数据呢?拖着这成百上千的字节拷贝来拷贝去吗?显然会浪费效率。那么怎么办?当然传递这些对象的首地址是一个办法,但这至少有两个缺点:
暴露了内核对象本身,使得程序(而不是操作系统内核)也可以任意地修改对象地内部状态(首地址都知道了,还有什么不能改的?),这显然是操作系统内核所不允许的;
操作系统有定期整理内存的责任,内存管理器经常在内存中来回移动对象,依此来满足各种应用程序的内存需要,对象被移动意味着它的地址变化了,如果地址总是如此变化,我们该到哪里去找该对象呢?
所以,Windows操作系统就采用进一步的间接:在进程的地址空间中设一张表,表里头专门保存一些编号和由这个编号对应一个地址,而由那个地址去引用实际的对象,这个编号跟那个地址在数值上没有任何规律性的联系,纯粹是个映射而已。这个编号就叫做"句柄"。
关于Windows句柄
Windows对象和句柄类型
Windows对象分为内核对象、GDI对象和User对象:
内核对象是不属于进程的,是属于windows内核的。进程只有一个内核对象句柄表,用来存放所有的内核对象句柄。所以,多个进程可以同时使用一个内核对象 ,只要有句柄即可。
对于GDI对象和User对象,他们是一个进程内部拥有的东西,不会被多个进程共有。GDI对象与绘图相关,而User对象与交互相关。
内核对象如文件、进程、线程等;GDI对象如字体、画笔、位图等;User对象如图标、菜单、窗体等,不同对象对应着不同类型的句柄,如文件句柄、窗体句柄等;
查看句柄
通过任务管理器
通过Process Explorer工具,点击进程右键、属性、性能选项卡查看:
Handles为内核对像的句柄,包括文件句柄,GDI 和User分别是GDI句柄和User句柄。
修改句柄
文件句柄数默认为512,GDI句柄默认为10000,User句柄默认为10000;
GDI和User句柄可通过注册表修改,修改“GDIProcessHandleQuota”与“USERProcessHandleQuota”注册表项:
关于Linux句柄
Linux中类似Windowns句柄的是fd,在Linux系统设计里面遵循一切都是文件的原则,即磁盘文件、目录、网络套接字、磁盘、管道等,所有这些都是文件,在我们进行打开的时候会返回一个fd,即是文件句柄,Linux不存在具体的句柄类型。
查看句柄
ulimit -n:查看当前用户单个进程能够打开的最大文件句柄数量(包括Socket链接),默认是1024;
lsof -n |awk '{print $2}'|sort|uniq -c |sort -nr|more:查看所有进程的句柄数量(降序排序),第一列是句柄数,第二列是PID;
lsof -n|awk '{print $2}'|sort|uniq -c|sort -nr|more |grep <pid>:查看某个进程的句柄数;
修改句柄
方法1:ulimit -HSn 4096:修改句柄数为4096,仅对当前进程有效,关闭或重启失效;
方法2:修改linux系统参数。vi /etc/security/limits.conf 添加
* soft nofile 2048
* hard nofile 32768
就可以将文件句柄限制统一改成软2048,硬32768,硬限制是实际的限制,而软限制,是warnning限制,只会做出warning ;
修改以后保存,注销当前用户,重新登录,执行ulimit -a 查看当前状态是否生效。
句柄问题
1. 句柄泄露:句柄只增不减,一般是由于各类资源未释放,如Socket、文件等;
2. too many files open:这里的file不只是指文件,包括Socket连接等,当句柄数超过了限制,进程将无法获取新的句柄,而从导致不能打开新的文件或者网络套接字,对于线上Server即会出现服务被拒绝的情况;