1、进程与线程 进程是惰性的,从来不执行任何东西,它只是一个线程的容器。线程必定是在某个进程的上下文中创建的, 而且其生命周期都在该进程中。因为句柄表是针对每一个进程的,因此同一个进程中的多个线程可以共享 内核对象句柄。进程运行需要占用许多的内存资源(加载DLL等),进程只需要一个内核对象和一个进程栈, 无需占用多少内存。 2、终止线程的几种方式: 2.1线程函数返回(强烈推荐) 2.2通过ExitThread函数杀死自己(自杀,不推荐) 终止线程运行,操作系统清理线程使用的系统资源,但是C/C++对象不会被析构。 2.3同一个进程或者另一个进程中的线程调用TerminateThread函数终止进程(他杀,不推荐) 和杀进程一样,TerminateThread是异步执行的,通知系统要终止线程,但在函数返回时并不能确定线程 已经被终止,可以使用WaitForSingleObject来等待; 如果是ExitThread终止线程,该线程的堆栈是会被清理掉的。但是如果使用TerminateThread,那么除非 拥有该线程的进程终止,否则系统不会销毁这个线程的堆栈(很重要,对于长时间运行的程序,如果我们 一直重复创建线程、杀死线程可能会造成内存不断的增长,最终程序可能崩溃)。被杀死线程的堆栈没有 被清理,其他线程依然可以正常访问。 另外,DLL通常在线程正常终止时收到通知,释放相关资源。但是,线程被TerminateThread强制杀死时, DLL不会收到通知,也就不能进行正常的清理工作。 2.4线程所在的进程终止运行 程序运行过程:C\C++运行库调初始化后调用main\WinMain函数,mian函数返回后,C\C++运行库调用ExitProcess 退出进程。在多个线程乐观运行时,需要在主线程返回之前处理好每个线程的终止过程。否则,其他正在 运行的线程会在C\C++运行库调用ExitProcess后突然死亡。 3、线程正常终止过程 (1)线程所拥有的用户对象句柄会被释放掉,一个线程有两个用户对象:窗口和挂钩。一旦线程退出,系统会销毁 其创建的窗口,并卸载由线程创建或者挂载的挂钩。其他对象只有在拥有线程的进程终止时才会被销毁; (2)线程的退出码从STILL_ACTIVE百年城传递给退出线程函数的代码; (3)线程的内核对象状态变为触发状态; (4)如果线程是进程中的最后一个活动线程,系统认为进程也终止了; (5)线程内核对象的应用计数-1。 其他线程可以通过GetExitCodeThread()来检查hThread所标识的那个线程是否已经终止运行,如果线程未终止则 返回STILL_ACTIVE。 4、内核中线程执行过程 调用内核API RtlUserThreadStart,传入线程函数地址fun和线程执行参数param,具体过程如下: (1)设置结构化异常处理帧; (2)系统调用线程函数fun传入参数param; (3)线程函数返回后,调用ExitThread并将函数返回值传给它。线程内核对象引用计数递减,线程停止执行; (4) 5、线程的内核对象何时会被销毁 众所周知,调用CloseHandle后内核对象引用计数递减,引用计数为0时内核对象将会被销毁。但是我们常常在 创建一个线程后立即用CloseHandle,然后线程还可以继续执行,内核对象没有被销毁吗? 原因在于,线程的内核对象创建时最初的引用计数为2,CloseHandle后只表示我们不再关心这个线程的句柄, 引用计数为1,所以内核对象并没被销毁。 除非线程终止,而且从CreateThread返回的线程句柄关闭,否则线程的内核对象不会被销毁!!!(这就告诉 我们,当我们不再需要使用一个线程句柄时,早点关闭它吧) 6、创建线程使用_beginthreadex而不是CreateThread CreateThread创建线程后,当线程需要调用含有_tiddata结构的函数时,C\C++运行库尝试通过TlsGetValue获取 线程数据块的地址,CreateThread并不初始化_tiddata结构因此返回NULL。这时C\C++会为线程分配并初始化一 个_tiddata块,然后通过TlsSetValue将这个结构与该线程相关联。然后,该线程调用任何的C\C++运行库函数都 可以使用该结构。 那么,问题来了。由于线程没有初始化异常处理帧,当线程使用了C\C++的signal函数,整个进程都会终止。还有 就是,如果线程不是通过_endthreadex来终止运行,数据块就不能被销毁,从而导致内存泄漏。(对于一个用 CreateThread创建的线程,我们真的不会用_endthreadex来终止) 当模块链接到C\C++的DLL版本库时,这个库会在线程终止时收到一个DLL_THREAD_DETACH通知,并会释放_tiddata 内存块(如果分配了的话)。 7、不要使用_beginthread()、_endthread(),早期函数局限性比较多,不建议使用。 8、了解自己身份 8.1 GetCurrentProcess 返回伪句柄 0xffffffff GetCurrentThread 返回伪句柄 0xfffffffe 调用CloseHandle关闭伪句柄时,返回FALSE,没有必要去关闭。 8.2伪句柄转真实句柄 使用DuplicateHandle函数,转换后的句柄使用完后需要CloseHandle关闭。
时间: 2024-10-11 08:47:25