第六章(5)C/C++运行期库

 

C/C++运行期库

若要使多线程 CC + +程序能够正确地运行,必须创建一个数据结构,并将它与使用C/C++运行期库函数的每个线程关联起来。

1_beginthreadex

_beginthreadex函数的参数列表与CreateThread函数的参数列表是相同的,但是参数名和类型并不完全相同(C / C + +运行期函数不对Windows数据类型有任何依赖)。_beginthreadex函数只存在于C/C++运行期库的多线程版本中,多线程应用程序必须显式转换到多线程的C/C++运行期库。

1_beginthreadex的一些要点:

每个线程均获得由C/C++运行期库的堆栈分配的自己的tiddata内存结构。(tiddata结构位于Mtdll.h文件中的Visual C++源代码中)。

传递给_beginthreadex的线程函数的地址保存在tiddata内存块中。传递给该函数的参数也保存在该数据块中。

_beginthreadex从内部调用CreateThread,因为这是操作系统了解如何创建新线程的唯一方法。

当调用CreateThread时,它被告知通过调用_threadstartex而不是pfnStartAddr来启动执行

新线程。还有,传递给线程函数的参数是tiddata结构而不是pvParam的地址。

如果一切顺利,就会像CreateThread那样返回线程句柄。如果任何操作失败了,便返回NULL

2_threadstartex的一些要点:

新线程开始从 BaseThreadStart函数(在kernel32.dll文件中)执行,然后转移到_threadstartex

到达该新线程的tiddata块的地址作为其唯一参数被传递给_threadstartex

TlsSetValue是个操作系统函数,负责将一个值与调用线程联系起来。这称为线程本地存储器(T L S _threadstartex函数将tiddata块与线程联系起来。

一个S E H帧被放置在需要的线程函数周围。这个帧负责处理与运行期库相关的事情.如运行期错误(和 C/C++运行期库的signal函数。(CreateThread函数创建线程,然后调用C/C++运行期库的signal函数,那么该函数就不能正确地运行。

调用必要的线程函数,传递必要的参数。(函数和参数的地址由 _ beginthreadex保存在tiddata块中。

必要的线程函数返回值被认为是线程的退出代码。

_threadstartex并不只是返回到BaseThreadStart。如果它准备这样做,那么线程就终止运行,它的退出代码将被正确地设置,但是线程的tiddata内存块不会被撤消。这将导致应用程序中出现一个漏洞。要防止这个漏洞,可以调用另一个C/C++运行期库函数_endthreadex ,并传递退出代码。

2_endthreadex

C运行期库的_getptd函数内部调用操作系统的TlsGetValue函数,该函数负责检索调用线程的tiddata内存块的地址。然后该数据块被释放,而操作系统的ExitThread函数被调用,以便真正撤消该线程。当然,退出代码要正确地设置和传递。

ExitThread 函数将撤消调用函数,并且不允许它从当前执行的函数返回。由于该函数不能返回,所以创建的任何 C + +对象都不会被撤消。它会使得线程的t i d d a t a内存块无法释放,这样,应用程序将会始终占用内存(直到整个进程终止运行为止)

C / C + +运行期库的启动代码为应用程序的主线程分配了数据块,并且对数据块进行了初始化,这样,主线程就能安全地调用 C / C + +运行期函数中的任何函数。当主线程从它的进入点函数返回时,C / C + +运行期库就会释放相关的数据块。此外,启动代码设置了相应的结构化异常处理代码,以便主线程能够成功地调用C / C + +运行期库的s i g n a l函数。

3_beginthread_endthread

_beginthread无法创建带有安全属性的新线程,无法创建暂停的线程,也无法获得线程的I D值。_endthread函数不带参数,线程的退出代码必须硬编码为0

原文地址:https://www.cnblogs.com/dyufei/p/2573999.html