先来看一看 _beginthread 与 _endthread 函数的源码

uintptr_t _beginthread(_beginthread_start_routine_t start_address, /* [in] Start address of routine that begins execution of new thread */unsigned int stack_size, /* [in] Stack size for new thread or 0 */void *arglist)           /* [in] Argument list to be passed to new thread or NULL */
{_beginthread_trampoline_t* trampoline;HANDLE thread;TRACE("(%p, %d, %p)\n", start_address, stack_size, arglist);trampoline = malloc(sizeof(*trampoline));if(!trampoline) {*_errno() = EAGAIN;return -1;}thread = CreateThread(NULL, stack_size, _beginthread_trampoline,trampoline, CREATE_SUSPENDED, NULL);if(!thread) {free(trampoline);*_errno() = EAGAIN;return -1;}trampoline->thread = thread;trampoline->start_address = start_address;trampoline->arglist = arglist;if(ResumeThread(thread) == -1) {free(trampoline);*_errno() = EAGAIN;return -1;}return (uintptr_t)thread;
}void CDECL _endthread(void)
{TRACE("(void)\n");/* FIXME */ExitThread(0);
}

可以看到, _endthread 函数非常简单,它就是直接调用 ExitThread 结束线程。
而 _beginthread 函数则复杂一点点,它调用 CreateThread 函数之前,先申请一个结构体,并将结构体作为参数传到线程的回调函数中。

我们再看看 _beginthread_trampoline_t 结构体,非常简单, 就是将用户传递的回调函数和参数保存起来了,CreateThread 返回的线程句柄也存放到该结构体中。

typedef struct {HANDLE thread;_beginthread_start_routine_t start_address;void *arglist;
} _beginthread_trampoline_t;

以上的代码并没有什么神奇之处,那么我们再来看看 _beginthread_trampoline 这个回调函数是怎么实现的

static DWORD CALLBACK _beginthread_trampoline(LPVOID arg)
{_beginthread_trampoline_t local_trampoline;thread_data_t *data = msvcrt_get_thread_data();memcpy(&local_trampoline,arg,sizeof(local_trampoline));data->handle = local_trampoline.thread;free(arg);local_trampoline.start_address(local_trampoline.arglist);return 0;
}

可以看到 _beginthread_trampoline 函数只做了三件事

  1. 取得 thread_data_t 指针,并把 线程句柄赋值给它
  2. 将 arg变量复制到栈上的 local_trampoline 中,并释放 arg
  3. 调起用户传递过来的回调函数

其中第二第三点并不难理解,不过我相信大部分人对第一点比较疑惑。它真的就是将线程句柄赋值到 thread_data_t 这个结构体这么简单吗?会不会存在别的含义?

再解开疑惑之前,我们先看看 thread_data_t 的数据结构:

struct __thread_data {DWORD                                 tid;HANDLE                                handle;int                                   thread_errno;unsigned long                         thread_doserrno;int                                   unk1;unsigned int                          random_seed;        /* seed for rand() */char                                  *strtok_next;        /* next ptr for strtok() */wchar_t                               *wcstok_next;        /* next ptr for wcstok() */unsigned char                         *mbstok_next;        /* next ptr for mbstok() */char                                  *strerror_buffer;    /* buffer for strerror */wchar_t                               *wcserror_buffer;    /* buffer for wcserror */char                                  *tmpnam_buffer;      /* buffer for tmpname() */wchar_t                               *wtmpnam_buffer;     /* buffer for wtmpname() */void                                  *unk2[2];char                                  *asctime_buffer;     /* buffer for asctime */wchar_t                               *wasctime_buffer;    /* buffer for wasctime */struct tm                             *time_buffer;        /* buffer for localtime/gmtime */char                                  *efcvt_buffer;       /* buffer for ecvt/fcvt */int                                   unk3[2];void                                  *unk4[4];int                                   fpecode;struct MSVCRT_threadmbcinfostruct     *mbcinfo;struct MSVCRT_threadlocaleinfostruct  *locinfo;BOOL                                  have_locale;int                                   unk5[1];terminate_function                    terminate_handler;unexpected_function                   unexpected_handler;_se_translator_function               se_translator;void                                  *unk6[3];int                                   unk7;EXCEPTION_RECORD                      *exc_record;void                                  *unk8[100];
};

这里有一个特别的地方,大家留意有注释的结构体成员,就拿 strtok_next 和 wcstok_next 这两个成员来说,注释解释它是为 strtok 函数和 wcstok 函数服务的,那么我们再来看看 strtok 函数 和 wcstok 函数 的源码

char * CDECL strtok( char *str, const char *delim )
{thread_data_t *data = msvcrt_get_thread_data();char *ret;if (!str)if (!(str = data->strtok_next)) return NULL;while (*str && strchr( delim, *str )) str++;if (!*str) return NULL;ret = str++;while (*str && !strchr( delim, *str )) str++;if (*str) *str++ = 0;data->strtok_next = str;return ret;
}wchar_t * CDECL wcstok( wchar_t *str, const wchar_t *delim )
{return wcstok_s(str, delim, &msvcrt_get_thread_data()->wcstok_next);
}

两个函数都调用了 msvcrt_get_thread_data 函数来取得 thread_data_t ,并且将本来应该放置于全局变量的数据,改为放置到了 thread_data_t 结构的成员中(在多线程技术还没出现之前,很多C库函数都是将一些函数成员设置为全局静态变量的,到了多线程时代,这些函数容易崩溃或者出现数据混乱的情况)。由此可见,_beginthread 相比 CreateThread 函数,其中一个优势就是在确保在调用过去的C库函数时,有安全性保障。所以在windows下使用多线程,应该优先考虑使用 _beginthread 或 _beginthreadex 函数。

接下来,让后我们看看 msvcrt_get_thread_data 一系列函数的实现:


/* Index to TLS */
static DWORD msvcrt_tls_index;inline BOOL msvcrt_init_tls(void)
{msvcrt_tls_index = TlsAlloc();if (msvcrt_tls_index == TLS_OUT_OF_INDEXES){ERR("TlsAlloc() failed!\n");return FALSE;}return TRUE;
}inline BOOL msvcrt_free_tls(void)
{if (!TlsFree(msvcrt_tls_index)){ERR("TlsFree() failed!\n");return FALSE;}return TRUE;
}thread_data_t *msvcrt_get_thread_data(void)
{thread_data_t *ptr;DWORD err = GetLastError();  /* need to preserve last error */if (!(ptr = TlsGetValue( msvcrt_tls_index ))){if (!(ptr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ptr) )))_amsg_exit( _RT_THREAD );if (!TlsSetValue( msvcrt_tls_index, ptr )) _amsg_exit( _RT_THREAD );ptr->tid = GetCurrentThreadId();ptr->handle = INVALID_HANDLE_VALUE;ptr->random_seed = 1;ptr->locinfo = MSVCRT_locale->locinfo;ptr->mbcinfo = MSVCRT_locale->mbcinfo;}SetLastError( err );return ptr;
}inline void msvcrt_free_tls_mem(void)
{thread_data_t *tls = TlsGetValue(msvcrt_tls_index);if (tls){CloseHandle(tls->handle);HeapFree(GetProcessHeap(),0,tls->efcvt_buffer);HeapFree(GetProcessHeap(),0,tls->asctime_buffer);HeapFree(GetProcessHeap(),0,tls->wasctime_buffer);HeapFree(GetProcessHeap(),0,tls->strerror_buffer);HeapFree(GetProcessHeap(),0,tls->wcserror_buffer);HeapFree(GetProcessHeap(),0,tls->time_buffer);//if(tls->have_locale) {//    free_locinfo(tls->locinfo);//    free_mbcinfo(tls->mbcinfo);//}}HeapFree(GetProcessHeap(), 0, tls);
}/* EOF */

上面的代码不多, 也不难,它就是通过 TLS(线程局部存储) 来取得并保存了一些线程信息,并且在 msvcrt_free_tls_mem 调用 CloseHandle(tls->handle) 来关闭线程句柄。

一般情况下,我们使用 CreateThread 后,会使用CloseHandle 关闭返回的句柄, 而这里已经帮我们关闭了,所以使用 _beginthread 创建的线程,不需要关闭句柄。

但是等等,我们貌似有些疑问还没有解决。。。
那就是 上面几个函数,在什么时候被调用呢?

首先跟大家说明一下,这些代码,是存在于 CRTDLL.DLL 函数,既然是动态库,就会有 dllmain 函数。答案就是 dllmain 函数里面

  1. 在 DLL_PROCESS_ATTACH (被进程加载时)调用 msvcrt_init_tls 初始化
  2. 在 DLL_PROCESS_DETACH (被进程卸载时)调用 msvcrt_free_tls 反初始化
  3. 在 DLL_THREAD_DETACH (进程内部调用了 ExitThread 函数) 调用 msvcrt_free_tls_mem 释放当前线程的 thread_data_t 。
  4. 在 DLL_THREAD_ATTACH (进程内部有线程创建时) 其实这个消息,它什么都没做。
  5. 至于 msvcrt_get_thread_data 函数,大家都看见了,它在 _beginthread 的回调函数中被调用
  6. 另外 使用 CreateThread 创建的线程,在结束时会默认调用 ExitThread 。所以,msvcrt_free_tls_mem 每次总是顺理成章地被调用,而保存在 thread_data_t 中的线程句柄,也在ExitThread 函数结束前被释放了。

<完>

_beginthread 与 _endthread 函数分析 (ReactOS版)相关推荐

  1. matlab 30案例 目录,MATLAB-智能算法30个案例分析-终极版(带目录).doc

    MATLAB-智能算法30个案例分析-终极版(带目录) MATLAB 智能算法30个案例分析(终极版) 1?基于遗传算法的TSP算法(王辉)? 2?基于遗传算法和非线性规划的函数寻优算法(史峰)? 3 ...

  2. Joomla远程代码执行漏洞分析小白版(小宇特详解)

    Joomla远程代码执行漏洞分析小白版(小宇特详解) 今天看了一下2021陇原战役WP,在看web方向的时候,看到pop链,想了解一下,后来又看到了p师傅在15年的一篇文章,在这里记录一下.这里主要是 ...

  3. python文档整理,Python官方文档内置函数整理Word版

    <Python官方文档内置函数整理Word版>由会员分享,可在线阅读,更多相关<Python官方文档内置函数整理Word版(6页珍藏版)>请在人人文库网上搜索. 1.传播优秀W ...

  4. Skype 协议分析(2006版)

    Skype 协议分析(2006版) 整理翻译:袁建明 Angel_YY@126.com 概要: Skype是创建Kazaa的组织在2003年开发的一个基于Peer-to-Peer(对等网络)的VoIP ...

  5. linux C函数之strdup函数分析【转】

    本文转载自:http://blog.csdn.net/tigerjibo/article/details/12784823 linux C函数之strdup函数分析 一.函数分析 1.函数原型: [c ...

  6. [C] zintrin.h: 智能引入intrinsic函数 V1.01版。改进对Mac OS X的支持,增加INTRIN_WORDSIZE宏...

    新版本-- http://www.cnblogs.com/zyl910/archive/2012/11/07/zintrin_v102.html [C] zintrin.h: 智能引入intrinsi ...

  7. 【Android 逆向】Dalvik 函数抽取加壳 ( 类加载流程分析 | Class.cpp#findClassNoInit 函数 | DexFile.cpp#dexFindClass 函数分析 )

    文章目录 前言 一.Class.cpp#dvmDefineClass 函数分析 二.Class.cpp#findClassNoInit 函数分析 三.DexFile.cpp#dexFindClass ...

  8. 【Android 逆向】Dalvik 函数抽取加壳 ( 类加载流程分析 | DexPathList#findClass 函数分析 | DexFile#loadClassBinaryName 函数 )

    文章目录 前言 一.DexPathList.java#findClass 类加载函数源码分析 二.DexFile.java#loadClassBinaryName 函数源码分析 前言 上一篇博客 [A ...

  9. 【Android 逆向】整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | DexPathList 构造函数分析 | makeDexElements 函数分析 )

    文章目录 前言 一.DexPathList 构造函数分析 二.DexPathList.makeDexElements 函数分析 三.Element 类分析 前言 上一篇博客 [Android 逆向]整 ...

  10. 【Android 逆向】Android 逆向通用工具开发 ( adb forward 网络端口重定向命令 | PC 端逆向程序主函数分析 )

    文章目录 前言 一.adb forward 网络端口重定向命令 二.PC 端逆向程序主函数分析 前言 本篇博客重点分析 PC 端 hacktool 模块 ; 一.adb forward 网络端口重定向 ...

最新文章

  1. 利用VSTS工具自动测试
  2. 基于Java语言构建区块链(四)—— 交易(UTXO)
  3. android 开发小工具,Android 开发者必备的八款小工具
  4. 0408互联网新闻 | 区块链医疗解决方案落地应用,阿里云、支付宝共同研发;安卓全球开发者峰会将于10月23-24日在加州召开...
  5. 2021曾都二中高考成绩查询入口,2021高考-随州设4个考区11个考点·
  6. 一个下课的时间带你手写promise!
  7. pytorch1.0 用torch script导出模型
  8. 苹果手机怎么拍星空_手机怎么拍星空
  9. Apache CXF实战之二 集成Sping与Web容器
  10. Flutter进阶第11篇: 调用原生硬件Api实现扫码 扫描条形码 扫描二维码
  11. 请求发送者与接收者解耦——命令模式(四)
  12. 交换机组播风暴_「广播风暴」CISCO交换机如何防止广播风暴 - seo实验室
  13. ios 获取沙盒文件名_iOS之沙盒路径
  14. lammps建模技巧:msi2lmp转换data文件结构错位解决办法
  15. 学UI设计需要会手绘吗
  16. linux 2.6内核镜像,Linux2.6内核镜像的构建过程
  17. boost asio——基于协程的TCP服务器
  18. Web信息架构——设计大型网站(第3版)
  19. 【GD32F427开发板试用】5. SPI驱动TFTLCD屏幕
  20. 鸿蒙商务旅行箱,全军出击!华为智选发布16款新品:鸿蒙分布式技术来了

热门文章

  1. python的快捷键总结
  2. 计算机包括台式机和笔记本,外星人Area-51M游戏笔记本评测:比台式机更强悍的笔记本...
  3. Chrome谷歌浏览器插件
  4. u-center出现r6002错误
  5. trend函数用oracle实现,Excel函数TREND函数的用法
  6. JDK9API网盘下载
  7. Vue JSON校验格式化编辑框 -- jsoneditor
  8. 51单片机秒表设计c语言版,51单片机秒表系统的设计
  9. 配置网络终端计算机,终端网络计算机概述.ppt
  10. 邱锡鹏神经网络怎么样,邱锡鹏 神经网络pdf