【C# 线程】转载 句柄的基本概念 .NET对象与Windows句柄
转载自:https://www.cnblogs.com/silverb/p/5300255.html
句柄的基本概念
1、句柄就是进程句柄表中的索引。
2、句柄是对进程范围内一个内核对象地址的引用,一个进程的句柄传给另一个进程是无效的。一个内核对象可用有多个句柄。
Windows之所以要设立句柄,根本上源于内存管理机制的问题,即虚拟地址。简而言之数据的地址需要变动,变动以后就需要有人来记录、管理变动,因此系统用句柄来记载数据地址的变更。
在.NET编程中,得益于有效的内存管理机制,对象的创建和使用比较方便,大多数情况下我们无须关心对象创建和分配内存的细节,也可以放心的把对象的清理交给自动垃圾回收来完成。由于.NET类库对系统底层对象进行了封装,我们也不需要调用Windows API来操作非托管对象。但不直接操作非托管对象,并不意味着程序不会间接创建这些对象,如果不了解.NET对象与非托管资源的关系,我们很有可能因为不恰当的使用这些托管对象,而导致非托管资源泄露。本文尝试说明Windows对象和句柄的基本概念,以及.NET编程中的对象与它们的关系,并结合一些简单的示例程序来探讨句柄泄露的话题。
一、什么是句柄?
Windows编程中,程序需要访问各种各样的资源,如文件、网络、窗口、图标和线程等。不同类型的资源被系统封装成不同的数据结构,当需要使用这些资源时,程序需要依据这些数据结构创建出不同的对象,当操作完毕并不再需要这些对象时,程序应当及时释放它们。在Windows中,应用程序不能直接在内存中操作这些对象,而是通过一系列公开的Windows API由对象管理器(Object Manager)来创建、访问、跟踪和销毁这些对象。当调用这些API创建对象时,它们并不直接返回指向对象的指针,而是会返回一个32位或64位的整数值,这个在进程或系统范围内唯一的整数值就是句柄(Handle)。随后程序再次访问对象,或者删除对象,都将句柄作为Windows API的参数来间接对这些对象进行操作。在这个过程中,句柄作为系统中对象的标识来使用。
对象管理器是系统提供的用来统一管理所有Windows内部对象的系统组件。这里所说的内部对象,不同于高级编程语言如C#中“对象”的概念,而是由Windows内核或各个组件实现和使用的对象。这些对象及其结构,要么不对用户代码公开,要么只能使用句柄由封装好的Windows API进行操作。C#编程中,多数情况下,我们并不需要与这些Windows API打交道,这是因为.NET类库对这些API又进行了封装,但我们的托管程序仍然会间接创建出很多Windows内部对象,并持有它们的句柄。
如上所说,句柄是一个32位或64位的整数值(取决于操作系统),所以在32位系统中,C#完全可以用int来表示一个句柄。但.NET提供了一个结构体System.IntPtr专门用来代表句柄或指针,在需要表示句柄,或者要在unsafe代码中使用指针时,应当使用IntPtr类型。
二、C#中创建文件句柄的过程
举例来说,文件属于一种非托管的系统资源。在C#中,可以用File类的静态方法Open来得到一个FileStream对象,来对磁盘文件进行读写操作。FileStream对象本身是托管对象,它是如何与文件这个非托管资源产生联系的呢?
大致说来,C#中打开文件的操作会经过下列步骤:
- 调用.NET静态方法System.IO.File.Open时,File类会创建一个FileStream对象并传入必要的参数,如文件路径,FileMode和FileAccess选项。FileMode枚举表明是希望创建新文件,打开已有文件,覆盖原有文件或是在原文件上追加新内容;FileAccess枚举表明是希望读文件、写文件或两者都有。
- 接着FileStream调用自己的Init方法进行初始化,在这个过程中,有更多细节需要考虑。为了创建一个文件,初始化方法需要更多额外的信息和检查,比如本进程在使用文件时是否允许其它进程读写文件,文件路径是否有效,是否有足够的权限,目标文件是否是允许被访问的文件类型,是否正确设置了FileMode和FileAccess选项的组合等。
- 完成这些必要的检查后,FileStream.Init调用Win32Native.SafeCreateFile方法。
- Win32Native类封闭了大量的Windows API,SafeCreateFile方法以P/Invoke的方式调用kernel32.dll中的CreateFile API,并返回SafeFileHandle。SafeFileHandle是一个有趣的类型,继承自SafeHandle,包含了真正的IntPtr类型的文件句柄。.NET的设计者有意让这个句柄字段对外不可见,但如果你非要拿到这个句柄值,SafeFileHandle也提供了DangerousGetHandle()方法满足你的要求:都告诉你Dangerous了,你自己看着办。
- 包含着文件句柄的SafeFileHandle会被返回并存放在FileStream对象中。随后的读取和写入操作,FileStream都会使用这个句柄与Windows API进行交互,直到最终关闭句柄。至始至终,我们的代码都无需直接关心句柄的存在,FileStream负责了绝大部分工作。
三、通过句柄操作对象的好处
Windows不允许应用程序直接访问内存中更底层的对象,而是由对象管理器统一管理,总的来说,至少有以下好处:
- 在操作系统层面上,为所有程序使用系统资源提供了统一的接口和机制。如果没有对象管理器,不同程序会有各种各样的实现方式来访问资源,并且这些代码散落在各种,难以规范,也无从协调解决资源的争用。
- 将需要在系统级别保护的对象隔离起来,提供更高安全性。
- 所有对系统关键资源的访问都经由对象管理器,使得系统可以方便的追踪和限制资源的使用,进行权限控制。
四、查看进程的句柄数量
到现在为止,本文讨论的全是看不见的概念,有必要来直观的看一下系统中的句柄使用情况。有多种方式可以查看进程的句柄使用情况,先从两个工具开始,Windows任务管理器和Process Explorer。
任务管理器默认不显示句柄数,需要在“查看”-“选择列”中勾选“句柄数”后,才会显示进程中当前打开的句柄数量。如下图所示,可以看到记事本进程当前打开59个句柄。
系统自带的任务管理器查看句柄数量很方便,但如果想知道这些句柄具体是什么,可以使用Process Explorer。Process Explorer是Windows Sysinternals工具包中的一个进程查看器,可以从这里下载。如果你看到的视图跟下图不同,可以点击View,选中Show Lower Pane,并在Lower Pane View中选择Handles。在列表中选择进程后,下方面板中会显示该进程中句柄的详细列表。
五、为什么关注句柄数
句柄指向的是诸如窗口、线程、文件、菜单、进程和定时器之类的系统资源,和所有被称为“资源”的事物一样,稀缺性是它们共同的特点。对于计算机和操作系统来讲,内存是一种稀缺资源,而所有的句柄和对象都存储在内存中。基于这个事实,操作系统不允许进程无限制的创建对象和句柄。对于任务管理器中的“句柄数”来讲,每一进程允许打开的句柄数理论上来讲可达2^24个,但由于内存的限制,实际数字大打折扣。在我的测试中,32位的.NET进程“句柄数”在达到1500万以上后,程序开始出现各种各样的问题。事实上绝大多数程序不会使用到这么多句柄,除非特殊需要,在软件编程中,如果自己的程序“句柄数”上千甚至是几千时,就需要引起特别注意,这一般说明程序中已经存在句柄泄露的情况。
你可能已经留意到,本文前面任务管理器中,除了显示进程的“句柄数”之外,还显示了“用户对象”和“GDI对象”的数量,它们属于另外两种句柄。具体的区别我们将在后面介绍,现在我们需要清楚的是,系统对于这两种对象同样设置了数量限制。对于“用户对象”和“GDI对象”来说,每个进程允许创建的数量上限是在注册表中设定的,分别是HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows中的USERProcessHandleQuota项和GDIProcessHandleQuota项,在Windows 7的32位操作系统上,两个项都被默认设置为10000。你可以更改这个设置,用户对象最多只能设定为18000个,GDI对象最多为65536个。但是改变这个设置是不被推荐的,一般情况下当你的应用程序需要用到超过10000个用户对象或GDI对象时,应该首先检查哪里出现了句柄泄露,而不是更改上限数量;另一方面,更改上限并不意味着应用程序就真的可以创建和使用这么多对象句柄,实际可用的数量同时受制于当前系统可用内存。
【C# 线程】转载 句柄的基本概念 .NET对象与Windows句柄的更多相关文章
- .NET对象与Windows句柄(三):句柄泄露实例分析
在上篇文章.NET对象与Windows句柄(二):句柄分类和.NET句柄泄露的例子中,我们有一个句柄泄露的例子.例子中多次创建和Dispose了DataReceiver和DataAnalyzer对象, ...
- .NET对象与Windows句柄(一):句柄的基本概念
在.NET编程中,得益于有效的内存管理机制,对象的创建和使用比较方便,大多数情况下我们无须关心对象创建和分配内存的细节,也可以放心的把对象的清理交给自动垃圾回收来完成.由于.NET类库对系统底层对象进 ...
- .NET对象与Windows句柄(二):句柄分类和.NET句柄泄露的例子
上一篇文章介绍了句柄的基本概念,也描述了C#中创建文件句柄的过程.我们已经知道句柄代表Windows内部对象,文件对象就是其中一种,但显然系统中还有更多其它类型的对象.本文将简单介绍Windows对象 ...
- Java并发编程(02):线程核心机制,基础概念扩展
本文源码:GitHub·点这里 || GitEE·点这里 一.线程基本机制 1.概念描述 并发编程的特点是:可以将程序划分为多个分离且独立运行的任务,通过线程来驱动这些独立的任务执行,从而提升整体的效 ...
- 第一节:《线程安全和锁Synchronized概念》
第一节:线程安全和锁Synchronized概念 一.进程与线程的概念 (1)在传统的操作系统中,程序并不能独立运行,作为资源分配和独立运行的基本单位都是进程. 在未配置 OS 的系统中,程序的执行方 ...
- 深入了解Windows句柄到底是什么
深入了解Windows句柄到底是什么 http://blog.csdn.net/wenzhou1219/article/details/17659485 总是有新入门的Windows程序员问我Wind ...
- 转:深入了解Windows句柄
深入了解Windows句柄到底是什么 转:http://blog.csdn.net/wenzhou1219/article/details/17659485 总是有新入门的Windows程序员问我Wi ...
- 【旧文章搬运】Windows句柄分配算法(一)
原文发表于百度空间,2009-04-04========================================================================== 分析了Wi ...
- 第9章 用内核对象进行线程同步(1)_事件对象(Event)
9.1 等待函数 (1)WaitForSingleObject(hObject,dwMilliseonds); ①dwMilliseconds为INFINITE时表示无限等待 ②dwMilliseco ...
随机推荐
- golang中结构体和结构体指针的内存管理
p1是结构体,p2是结构体指针. 2. 声明并赋值结构体和结构体指针 package main import "fmt" type Person struct { name str ...
- Error building SqlSession. ### The error may exist in dao/UserMapper.xml ### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration(2 字节的 UTF-8 序列的字节 2 无效。)
关于在学习Mybatis框架时运行报错 Caused by: org.apache.ibatis.exceptions.PersistenceException: ### Error building ...
- K8s QoS Pod资源服务质量控制
Kubernetes 中如果一个 Node 节点上的 Pod 占用资源过多并且不断飙升导致 Node 节点资源不足,可能会导致为了保证节点可用,将容器被杀掉.在遇见这种情况时候,我们希望先杀掉那些不太 ...
- springboot 配置mybatis 配置mapper.xml
# 插件 进行配置 也可以用yml # 1. 配置 Tomcat 修改端口号 server.port=8848 server.context-path=/zxf #2.配置数据源 spring.dat ...
- Spring源码-IOC部分-自定义IOC容器及Bean解析注册【4】
实验环境:spring-framework-5.0.2.jdk8.gradle4.3.1 Spring源码-IOC部分-容器简介[1] Spring源码-IOC部分-容器初始化过程[2] Spring ...
- python编写购物车新写法
用另一种方式完成购物车的功能实现 #!/usr/bin/python zijin = input("请输入资金:") if zijin.isdigit(): zijin = int ...
- PHP获取日期和时间:
转载请注明来源:https://www.cnblogs.com/hookjc/ 使用函式 date() 实现 <?php echo $showtime=date("Y-m-d H:i: ...
- JVM学习十五 - (复习)类加载的时机、类加载过程、类加载器
一.类加载的时机 类的生命周期 类从被加载到虚拟机内存开始,到卸载出内存为止,它的整个生命周期包括以下 7 个阶段: 加载 验证 准备 解析 初始化 使用 卸载 验证.准备.解析 3 个阶段统称为连接 ...
- Tomcat部署时war和war exploded区别以及打包后路径问题
感谢原文作者:keven_deng 原文链接:https://blog.csdn.net/keven_deng/article/details/104830664 war和war exploded的区 ...
- 7月3日下午 微擎芸众商城 设计思路 - laravel路由底层源码解读
学习参考文章 https://learnku.com/articles/13622/the-principle-of-laravel-routing-execution <?phpnamespa ...