多线程--内核对象和句柄泄露&CloseHandle

来源:互联网 发布:淘宝店店铺标志图片 编辑:程序博客网 时间:2024/06/05 17:52

多线程--内核对象和句柄泄露&CloseHandle

1.什么是内核对象?

内核对象是操作系统分配的一个内存块,该内存块是一个数据结构,用于管理对象的相关信息。

当应用程序要与系统设备交互的时候,将使用内核对象。而出于安全考虑,进程是不能直接操作内核对象的,于是操作系统提供相应的函数来访问它们。

存取符号对象、事件对象、文件对象、作业对象、互斥对象等都是内核对象。编程中需要经常创建、打开和操作它们。


2.什么是句柄?

通俗来说,句柄就是windows用来标示应用程序所建立或者使用的对象的整数。也可以将句柄简单理解为使用的对象的指针。windows使用各种各样的句柄来标示诸如应用程序中的窗口、控件、位图、GDI对象等等。


3.句柄泄露

当应用程序使用完内核对象后需要释放资源并关闭内核对象,如果没有使用CloseHandle,则使得当前进程无法再打开对应的内核对象,从系统的角度看,这些无法打开的内核对象将占用大量的内存,于是就出现了句柄泄露。

以下以CloseHandle来说明句柄泄露的问题。

===========================================================================

CloseHandle函数处理不再使用的核心对象。这是因为,一个核心对象,在被创建出来以后,可以被多个拥有者拥有,核心对象是由操作系统来处理的,因此,可以跨线程,跨进程。

    为了实现这种特性,对于核心对象,有一个“引用计数”的计数,具体地说就是:跟随一个核心对象的还有一个整数,在核心对象被创建出来时,这个数为1,其他的拥有者通过某种方式“拥有”了这个对象,这个数值加1;当拥有者释放了其拥有权时,该数减1;当该数为0时,表示没有任何拥有者拥有该对象,则操作系统负责销毁该对象。整个过程是操作系统负责的,对程序员来说是透明的。

    CloseHandle函数的作用是负责释放某个拥有者对核心对象的拥有权(计数减1)。

因此,CloseHandle并没有将该句柄对应的对象删掉,而仅仅将引用计数减一。当引用计数为0时,系统会负责将该对象删掉。

================================================

Closing a thread handle does not terminate the associated thread. To remove a thread object, you must terminate the thread, then close all handles to the thread.

================================================

1,线程和线程句柄(Handle)不是一个东西,线程是在cpu上运行的,线程句柄是一个内核对象。我们可以通过句柄来操作线程,但是线程的生命周期和线程句柄的生命周期不一样的。线程的生命周期就是线程函数从开始执行到return,线程句柄的生命周期是从CreateThread返回到你CloseHandle()。

2,所有的内核对象(包括线程Handle)都是系统资源,用了要还的,也就是说用完后一定要closehandle关闭之,如果不这么做,你系统的句柄资源很快就用光了。

所以
CloseHandel(ThreadHandle );
只是关闭了一个线程句柄对象,表示我不再使用该句柄,即不对这个句柄对应的线程做任何干预了。并没有结束线程。

如果你觉得多了一个变量,也可以写为:
CloseHandel(CreateThread(NULL,0,.....));

================================================================

《windows核心编程》上说调用closehandle(HANDLE)表示创建者放弃对该内核对象的操作。如果该对象的引用对象记数为0就撤消该对象。

====================================================================

CreateThread后那个线程的引用计数不是1,而是2。  
    
  creating   a   new   process   causes   the   system   to   create   a   process   kernel   object     
  and   a   thread   kernel   object.   At   creation   time,   the   system   gives   each   object     
  an   initial   usage   count   of   1.   Then,   just   before   CreateProcess   returns,   the     
  function   opens   the   process   object   and   the   thread   object   and   places   the     
  process-relative   handles   for   each   in   the   hProcess   and   hThread   members   of     
  the   PROCESS_INFORMATION   structure.   When   CreateProcess   opens   these   objects     
  internally,   the   usage   count   for   each   becomes   2.

=================================================================

创建新的进程后,记数初始化为1,而函数需要返回进程内核对象的句柄,相当于打开一次新创建的类核对象,记数再加1

=================================================================

在线程创建后马上调用CloseHandle()是个良好的做法,这里不会影响线程的执行,就是因为即使你close了这个handle,它的内部记数也不为零.   但如果你不关,在线程结束后,那个线程对象将滞留于内存中,也就是说你有handle   leak.  
    
  返回这个handle给你,是让你有机会对这个线程实施外部动作,诸如waitforsingleobject之类.  

==================================================================

CloseHandle的功能是关闭一个打开的对象句柄,该对象句柄可以是线程句柄,也可以是进程、信号量等其他内核对象的句柄,而ExitThread的功能是终止一个线程,它所接受的参数是一个线程的退出码。   
    
  通过调用CloseHandle可以告知系统,已经完成了对某一内核对象的操作,该函数首先检查调用进程的句柄表,来确认进程是否对该句柄所指向的对象有访问权,如果句柄无效则返回FALSE,如果有效,系统将得到该内核对象的数据结构的地址,把结构中的使用计数成员减1,如果计数变为0,则将从内核中释放该内核对象。   
    
  如果计数还未到0,就意味着还有其他的进程在使用这个内核对象,那么它就不会被释放。   
    
  ExitThread是推荐使用的结束一个线程的方法,当调用该函数时,当前线程的栈被释放,然后线程终止,相对于TerminateThread函数来说,这样做能够更好地完成附加在该线程上的DLL的清除工作。   
    
  如果需要进一步的信息,您可以参看:   
  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/handobj_289x.asp   
  和http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/prothred_531g.asp   
  等有关的MSDN信息。

==================================================================

线程作为一种资源创建后不只被创建线程引用,我想系统自身为了管理线程也会有一个引用,所以用户线程释放线程句柄后,引用计数也不会是零。引用计数是资源自我管理的一种机制,资源本身以引用计数为零来得知别人不再需要自己,从而把自己kill掉。

=================================================================