VS下EXE可执行文件启动代码剖析(2)_mtinit函数
紧接上文,来看看另外一个函数_mtinit,这个函数定义在tidtable.c文件中
/****
*_mtinit() - Init multi-thread data bases
*
*Purpose:
* (1) Call _mtinitlocks to create/open all lock semaphores.
* (2) Allocate a TLS index to hold pointers to per-thread data
* structure.
*
* NOTES:
* (1) Only to be called ONCE at startup
* (2) Must be called BEFORE any mthread requests are made
*
*Entry:
* <NONE>
*Exit:
* returns FALSE on failure
*
*Uses:
* <any registers may be modified at init time>
*
*Exceptions:
*
*******************************************************************************/
int __cdecl _mtinit (void)
{_ptiddata ptd; /_tiddata结构体的指针#ifdef _M_IX86/** Initialize fiber local storage function pointers.*/HINSTANCE hKernel32 = GetModuleHandleW(L"KERNEL32.DLL");if (hKernel32 == NULL) {_mtterm();return FALSE; /* fail to load DLL */}gpFlsAlloc = (PFLS_ALLOC_FUNCTION)GetProcAddress(hKernel32,"FlsAlloc");gpFlsGetValue = (PFLS_GETVALUE_FUNCTION)GetProcAddress(hKernel32,"FlsGetValue");gpFlsSetValue = (PFLS_SETVALUE_FUNCTION)GetProcAddress(hKernel32,"FlsSetValue");gpFlsFree = (PFLS_FREE_FUNCTION)GetProcAddress(hKernel32,"FlsFree");if (!gpFlsAlloc || !gpFlsGetValue || !gpFlsSetValue || !gpFlsFree) {gpFlsAlloc = (PFLS_ALLOC_FUNCTION)__crtTlsAlloc;gpFlsGetValue = (PFLS_GETVALUE_FUNCTION)TlsGetValue;gpFlsSetValue = (PFLS_SETVALUE_FUNCTION)TlsSetValue;gpFlsFree = (PFLS_FREE_FUNCTION)TlsFree; }//填充这几个全局函数指针 Fls系列是纤程的局部储存函数,区别于线程局部储存Tls系列/** Allocate and initialize a TLS index to store FlsGetValue pointer* so that the FLS_* macros can work transparently*/if ( (__getvalueindex = TlsAlloc()) == TLS_OUT_OF_INDEXES ||!TlsSetValue(__getvalueindex, (LPVOID)gpFlsGetValue) ) {return FALSE; //初始化全局线程局部标识__getvalueindex,用来标识FlsGetValue函数的指针}
#endif /* _M_IX86 */_init_pointers(); /* initialize global function pointers */ 初始化一些全局函数的指针#ifdef _M_IX86/** Encode the fiber local storage function pointers*/gpFlsAlloc = (PFLS_ALLOC_FUNCTION) EncodePointer(gpFlsAlloc);gpFlsGetValue = (PFLS_GETVALUE_FUNCTION) EncodePointer(gpFlsGetValue);gpFlsSetValue = (PFLS_SETVALUE_FUNCTION) EncodePointer(gpFlsSetValue);gpFlsFree = (PFLS_FREE_FUNCTION) EncodePointer(gpFlsFree); //对函数指针加密
#endif /* _M_IX86 *//** Initialize the mthread lock data base*/if ( !_mtinitlocks() ) {_mtterm();return FALSE; /* fail to load DLL */ 初始化多线程同步时用到的一个结构}/** Allocate a TLS index to maintain pointers to per-thread data*/if ( (__flsindex = FLS_ALLOC(&_freefls)) == FLS_OUT_OF_INDEXES ) {_mtterm();return FALSE; /* fail to load DLL */ //__flsindex是一个全局的线程局部储存标识,标识每个线程的tiddate}/** Create a per-thread data structure for this (i.e., the startup)* thread.*/if ( ((ptd = _calloc_crt(1, sizeof(struct _tiddata))) == NULL) ||!FLS_SETVALUE(__flsindex, (LPVOID)ptd) ) //为tiddate结构分配对空间 {_mtterm();return FALSE; /* fail to load DLL */}/** Initialize the per-thread data*/_initptd(ptd,NULL); //初始化tiddate结构ptd->_tid = GetCurrentThreadId();ptd->_thandle = (uintptr_t)(-1);return TRUE;
}
从该函数的注释看这个函数主要做了两方面的工作
* (1) Call _mtinitlocks to create/open all lock semaphores.
* (2) Allocate a TLS index to hold pointers to per-thread data
* structure.
1、调用_mtinitlocks建立一个临界区对象表,该对象表保存一组CRITICAL_SECTION对象的指针,这些指针的一部分在调用该函数的时候初始化,他们被初始化为指向一个静态的CRITICAL_SECTION全局数组,这些临界区对象用来保证多线程环境下调用C/C++标准库函数时候能够多线程互斥,从而保证这些库函数对一些全局静态数据的访问能够在多线程环境下稳定工作,下文详细分析。
2、分配一个线程局部索引标记,为每个线程存储一个_tiddate结构体,该结构体同样是为C/C++标准库函数在多线程环境下工作提供支持,一些函数的实现中,用每个线程的_tiddate结构中的数据来当做全局变量工作。
首先 _ptiddata ptd;这里是一个结构体的指针 typedef struct _tiddata * _ptiddata;
_tiddata是一个在crt中很重要的结构体,每个程序的主线程以及使用_beginthreadex来启动的线程都默认拥有这样一个结构体,通过线程本地储存来保存它的指针
他定义在mtdll.h文件中
/* Structure for each thread's data */struct _tiddata {unsigned long _tid; /* thread ID */uintptr_t _thandle; /* thread handle */int _terrno; /* errno value */unsigned long _tdoserrno; /* _doserrno value */unsigned int _fpds; /* Floating Point data segment */unsigned long _holdrand; /* rand() seed value */char * _token; /* ptr to strtok() token */wchar_t * _wtoken; /* ptr to wcstok() token */unsigned char * _mtoken; /* ptr to _mbstok() token *//* following pointers get malloc'd at runtime */char * _errmsg; /* ptr to strerror()/_strerror() buff */wchar_t * _werrmsg; /* ptr to _wcserror()/__wcserror() buff */char * _namebuf0; /* ptr to tmpnam() buffer */wchar_t * _wnamebuf0; /* ptr to _wtmpnam() buffer */char * _namebuf1; /* ptr to tmpfile() buffer */wchar_t * _wnamebuf1; /* ptr to _wtmpfile() buffer */char * _asctimebuf; /* ptr to asctime() buffer */wchar_t * _wasctimebuf; /* ptr to _wasctime() buffer */void * _gmtimebuf; /* ptr to gmtime() structure */char * _cvtbuf; /* ptr to ecvt()/fcvt buffer */unsigned char _con_ch_buf[MB_LEN_MAX];/* ptr to putch() buffer */unsigned short _ch_buf_used; /* if the _con_ch_buf is used *//* following fields are needed by _beginthread code */void * _initaddr; /* initial user thread address */void * _initarg; /* initial user thread argument *//* following three fields are needed to support signal handling and* runtime errors */void * _pxcptacttab; /* ptr to exception-action table */void * _tpxcptinfoptrs; /* ptr to exception info pointers */int _tfpecode; /* float point exception code *//* pointer to the copy of the multibyte character information used by* the thread */pthreadmbcinfo ptmbcinfo;/* pointer to the copy of the locale informaton used by the thead */pthreadlocinfo ptlocinfo;int _ownlocale; /* if 1, this thread owns its own locale *//* following field is needed by NLG routines */unsigned long _NLG_dwCode;/** Per-Thread data needed by C++ Exception Handling*/void * _terminate; /* terminate() routine */void * _unexpected; /* unexpected() routine */void * _translator; /* S.E. translator */void * _purecall; /* called when pure virtual happens */void * _curexception; /* current exception */void * _curcontext; /* current exception context */int _ProcessingThrow; /* for uncaught_exception */void * _curexcspec; /* for handling exceptions thrown from std::unexpected */
#if defined (_M_IA64) || defined (_M_AMD64)void * _pExitContext;void * _pUnwindContext;void * _pFrameInfoChain;unsigned __int64 _ImageBase;
#if defined (_M_IA64)unsigned __int64 _TargetGp;
#endif /* defined (_M_IA64) */unsigned __int64 _ThrowImageBase;void * _pForeignException;
#elif defined (_M_IX86)void * _pFrameInfoChain;
#endif /* defined (_M_IX86) */_setloc_struct _setloc_data;void * _reserved1; /* nothing */void * _reserved2; /* nothing */void * _reserved3; /* nothing */
#ifdef _M_IX86void * _reserved4; /* nothing */void * _reserved5; /* nothing */
#endif /* _M_IX86 */int _cxxReThrow; /* Set to True if it's a rethrown C++ Exception */unsigned long __initDomain; /* initial domain used by _beginthread[ex] for managed function */
};typedef struct _tiddata * _ptiddata;
都有英文注释,不翻译了
这个结构体的阐述放到后面
下面开始一点一点的来看_mtinit 做了哪些工作
首先 初始化纤程本地存储 相关的几个函数指针
gpFlsAlloc gpFlsGetValue gpFlsSetValue gpFlsFree 因为这一系列函数在VISTA 和Server 2003及之后才支持,因此定义了在宏#ifdef _M_IX86内,在32位系统中从kernel32.dll中动态获取函数地址,并且加密该函数指针
gpFlsAlloc = (PFLS_ALLOC_FUNCTION) EncodePointer(gpFlsAlloc);gpFlsGetValue = (PFLS_GETVALUE_FUNCTION) EncodePointer(gpFlsGetValue);gpFlsSetValue = (PFLS_SETVALUE_FUNCTION) EncodePointer(gpFlsSetValue);gpFlsFree = (PFLS_FREE_FUNCTION) EncodePointer(gpFlsFree);
在mtdll.c文件中有这些函数指针怎么使用的宏定义
#ifdef _M_IX86extern PFLS_ALLOC_FUNCTION gpFlsAlloc;
extern PFLS_GETVALUE_FUNCTION gpFlsGetValue;
extern PFLS_SETVALUE_FUNCTION gpFlsSetValue;
extern PFLS_FREE_FUNCTION gpFlsFree;#define FLS_ALLOC(callback) (((PFLS_ALLOC_FUNCTION) DecodePointer(gpFlsAlloc))(callback))
#define FLS_GETVALUE ((PFLS_GETVALUE_FUNCTION)TlsGetValue(__getvalueindex))
#define FLS_SETVALUE(index, value) (((PFLS_SETVALUE_FUNCTION) DecodePointer(gpFlsSetValue))(index, value))
#define FLS_FREE(index) (((PFLS_FREE_FUNCTION) DecodePointer(gpFlsFree))(index))#else /* _M_IX86 */#define FLS_ALLOC(callback) FlsAlloc(callback)
#define FLS_GETVALUE(index) FlsGetValue(index)
#define FLS_SETVALUE(index, value) FlsSetValue(index, value)
#define FLS_FREE(index) FlsFree(index)
当32位编译模式的时候 解密函数指针并调用,在64位模式时候直接调用Kernel32.dll导出的接口
然后是
_init_pointers(); /* initialize global function pointers */ 初始化一些全局函数的指针
该函数定义在crt0dat.c文件中
void __cdecl _init_pointers() {void *enull = _encoded_null();_initp_heap_handler(enull);_initp_misc_invarg(enull);_initp_misc_purevirt(enull);_initp_misc_rand_s(enull);_initp_misc_winsig(enull);_initp_eh_hooks(enull);
}
_CRTIMP void * __cdecl _encoded_null()
{return EncodePointer(NULL); //返回一个对NULL的加密表示空的函数指针
}
1、_initp_heap_handler(enull); 定义在handler.cpp文件中
/* pointer to old-style C++ new handler */
_PNH _pnhHeap; //typedef int (__clrcall * _PNH)( size_t );一个函数指针/***
*_initp_heap_handler()
*
*Purpose:
*
*******************************************************************************/extern "C"
void __cdecl _initp_heap_handler(void *enull)
{_pnhHeap = (_PNH) enull;
}
_pnhHeap 是一个函数指针,这里将它设置成一个空的加密指针,该函数指针可以用_set_new_handler来设置它的值,这个函数用作NEW失败的时候调用,可以由我们来指定完成一些工作,关于这个的详细过程可以参考 对new的几种形式的一些认识
2、_initp_misc_invarg(enull); 定义在invarg.c文件中
/* global variable which stores the user handler */_invalid_parameter_handler __pInvalidArgHandler;
//typedef void (__cdecl *_invalid_parameter_handler)(const wchar_t *, const wchar_t *, const wchar_t *, unsigned int, uintptr_t);extern "C"
void _initp_misc_invarg(void* enull)
{__pInvalidArgHandler = (_invalid_parameter_handler) enull;
}
__pInvalidArgHandler函数指针,这里初始化为空加密,它的作用是当传递给一个CRT函数无效的参数时被调用,这个函数指针可以由 _set_invalid_parameter_handler 来设置,
3、 _initp_misc_purevirt(enull); 在inithelp.c文件中定义
_purecall_handler __pPurecall= NULL;/***
*void _initp_misc_purevirt(void) -
*
*Purpose:
* Initialize the __pPurecall function pointer
*
*Entry:
* The per-process encoded value for the null pointer.
*
*Exit:
* Never returns
*
*Exceptions:
*
*******************************************************************************///extern "C"
void __cdecl _initp_misc_purevirt(void* enull)
{__pPurecall = (_purecall_handler) enull;
}
__pPurecall全局函数指针,它的作用是当调用一个纯虚函数时候被触发,这个指针可以通过_set_purecall_handler来设置成我们自己的函数过程
4、 _initp_misc_rand_s(enull); 在Rand_s.c文件中定义
typedef BOOL (APIENTRY *PGENRANDOM)( PVOID, ULONG );static PGENRANDOM g_pfnRtlGenRandom;void __cdecl _initp_misc_rand_s(void* enull)
{g_pfnRtlGenRandom = (PGENRANDOM) enull;
}
g_pfnRtlGenRandom 全局函数指针,它的作用是返回一个无符号的随机整数,这个指针在rand_s函数中被替换为ADVAPI32.DLL导出的RtlGenRandom函数,参考MSDN 中rand_s函数的使用
5、_initp_misc_winsig(enull); 定义在Winsig.c文件中
/** variables holding action codes (and code pointers) for SIGINT, SIGBRK,* SIGABRT and SIGTERM.** note that the disposition (i.e., action to be taken upon receipt) of* these signals is defined on a per-process basis (not per-thread)!!*/static _PHNDLR ctrlc_action = SIG_DFL; /* SIGINT */
static _PHNDLR ctrlbreak_action = SIG_DFL; /* SIGBREAK */
static _PHNDLR abort_action = SIG_DFL; /* SIGABRT */
static _PHNDLR term_action = SIG_DFL; /* SIGTERM */void __cdecl _initp_misc_winsig(void* enull)
{ctrlc_action = (_PHNDLR) enull; /* SIGINT */ctrlbreak_action = (_PHNDLR) enull; /* SIGBREAK */abort_action = (_PHNDLR) enull; /* SIGABRT */term_action = (_PHNDLR) enull; /* SIGTERM */
}
_initp_misc_winsig 初始化这四个异常函数指针,这个四个函数指针在相应的 信号处理例程发生异常时候被调用
6、_initp_eh_hooks 没有找这个函数的实现
紧接着是
if ( !_mtinitlocks() ) {_mtterm();return FALSE; /* fail to load DLL */}
_mtinitlocks 这个函数的实现在 Mlock.c文件中
/***
*_mtinitlocks() - Initialize multi-thread lock scheme
*
*Purpose:
* Perform whatever initialization is required for the multi-thread
* locking (synchronization) scheme. This routine should be called
* exactly once, during startup, and this must be before any requests
* are made to assert locks.
*
* NOTES: In Win32, the multi-thread locks are created individually,
* each upon its first use. That is when any particular lock is asserted
* for the first time, the underlying critical section is then allocated,
* initialized and (finally) entered. This allocation and initialization
* is protected under _LOCKTAB_LOCK. It is _mtinitlocks' job to set up
* _LOCKTAB_LOCK.
*
* All other named (non-FILE) locks are also preallocated in _mtinitlocks.
* That is because a failure to allocate a lock on its first use in _lock
* triggers a fatal error, which cannot be permitted since that can bring
* down a long-lived app without warning.
*
*Entry:
* <none>
*
*Exit:
* returns FALSE on failure
*
*Exceptions:
*
*******************************************************************************/int __cdecl _mtinitlocks (void)
{int locknum;int idxPrealloc = 0;/** Scan _locktable[] and allocate all entries marked lkPrealloc.*/for ( locknum = 0 ; locknum < _TOTAL_LOCKS ; locknum++ ) {if ( _locktable[locknum].kind == lkPrealloc ) {_locktable[locknum].lock = &lclcritsects[idxPrealloc++];if ( !InitializeCriticalSectionAndSpinCount( _locktable[locknum].lock,_CRT_SPINCOUNT )){_locktable[locknum].lock = NULL;return FALSE;}}}return TRUE;
}
这里引用到了两个全局的静态数组_locktable和 lclcritsects,
#define NUM_STD_FILE_LOCKS 3/** _DEBUG_LOCK is preallocated in _DEBUG & not in Retail*/#ifdef _DEBUG
#define NUM_NON_PREALLOC_LOCKS 4
#else /* _DEBUG */
#define NUM_NON_PREALLOC_LOCKS 5
#endif /* _DEBUG */#define NUM_PREALLOC_LOCKS \( _STREAM_LOCKS + NUM_STD_FILE_LOCKS - NUM_NON_PREALLOC_LOCKS )
static CRITICAL_SECTION lclcritsects[NUM_PREALLOC_LOCKS];/** Lock Table* This table contains a pointer to the critical section management structure* for each lock.** Locks marked lkPrealloc have their critical sections statically allocated* and initialized at startup in _mtinitlocks. Locks marked lkNormal must* be allocated when first used, via a call to _mtinitlocknum.*/
static struct {PCRITICAL_SECTION lock;enum { lkNormal = 0, lkPrealloc, lkDeleted } kind;
} _locktable[_TOTAL_LOCKS] = {{ NULL, lkPrealloc }, /* 0 == _SIGNAL_LOCK */{ NULL, lkPrealloc }, /* 1 == _IOB_SCAN_LOCK */{ NULL, lkNormal }, /* 2 == _TMPNAM_LOCK - not preallocated */{ NULL, lkPrealloc }, /* 3 == _CONIO_LOCK */{ NULL, lkPrealloc }, /* 4 == _HEAP_LOCK */{ NULL, lkNormal }, /* 5 == _UNDNAME_LOCK - not preallocated */{ NULL, lkPrealloc }, /* 6 == _TIME_LOCK */{ NULL, lkPrealloc }, /* 7 == _ENV_LOCK */{ NULL, lkPrealloc }, /* 8 == _EXIT_LOCK1 */{ NULL, lkNormal }, /* 9 == _POPEN_LOCK - not preallocated */{ NULL, lkPrealloc }, /* 10 == _LOCKTAB_LOCK */{ NULL, lkNormal }, /* 11 == _OSFHND_LOCK - not preallocated */{ NULL, lkPrealloc }, /* 12 == _SETLOCALE_LOCK */{ NULL, lkPrealloc }, /* 13 == _MB_CP_LOCK */{ NULL, lkPrealloc }, /* 14 == _TYPEINFO_LOCK */
#ifdef _DEBUG{ NULL, lkPrealloc }, /* 15 == _DEBUG_LOCK */
#else /* _DEBUG */{ NULL, lkNormal }, /* 15 == _DEBUG_LOCK */
#endif /* _DEBUG */{ NULL, lkPrealloc }, /* 16 == _STREAM_LOCKS+0 - stdin */{ NULL, lkPrealloc }, /* 17 == _STREAM_LOCKS+1 - stdout */{ NULL, lkPrealloc }, /* 18 == _STREAM_LOCKS+2 - stderr */
/* { NULL, lkNormal }, /* ... */
};
可以看到_locktable 是个结构体数组,该类型的结构体有一个临界区对象指针PCRITICAL_SECTION lock;,和一个枚举型变量enum { lkNormal = 0, lkPrealloc, lkDeleted } kind构成。lclcritsects则是一个CRITICAL_SECTION临界区对象数组,该数组的大小为NUM_PREALLOC_LOCKS
#define NUM_PREALLOC_LOCKS \( _STREAM_LOCKS + NUM_STD_FILE_LOCKS - NUM_NON_PREALLOC_LOCKS )#define _STREAM_LOCKS 16 /* Table of stream locks */
#define NUM_STD_FILE_LOCKS 3#ifdef _DEBUG
#define NUM_NON_PREALLOC_LOCKS 4
#else /* _DEBUG */
#define NUM_NON_PREALLOC_LOCKS 5
#endif /* _DEBUG */
在Debug模式下是15 在release下是14 刚好和 _locktable 数组中 枚举量为lkPrealloc的元素数目相等。根据结构体的说 lkNormal类型的在第一次使用时为其分配内存,而lkPrealloc则在程序启动的时候为其分配内存并初始化
for ( locknum = 0 ; locknum < _TOTAL_LOCKS ; locknum++ ) {
if ( _locktable[locknum].kind == lkPrealloc ) {
_locktable[locknum].lock = &lclcritsects[idxPrealloc++]; //将类型为lpPrealloc类型的,让指针指向clcritsects数组对应的元素
if ( !InitializeCriticalSectionAndSpinCount( _locktable[locknum].lock,// 用InitializeCriticalSectionAndSpinCount初始化该临界区对象
_CRT_SPINCOUNT )) //_CRT_SPINCOUNT 的值为4000
{
_locktable[locknum].lock = NULL;
return FALSE;
}
}
}
InitializeCriticalSectionAndSpinCount 跟InitializeCriticalSection的区别在于,前者在尝试获取临街区代码控制权时有个数量的循环尝试过程,每次占用很少的CPU时钟周期,在循环完毕后才进入线程休眠状态,而后者如果获取不到权限直接进入休眠,线程休眠耗用的CPU时钟周期很大,因此在很轻量级的互斥中,如对单个全局变量的访问时,选择前者更为效率。
这个18临界区对象都有各自的用处,他们通过_lock和_unlock两个接口函数来使用
***
* _lock - Acquire a multi-thread lock
*
*Purpose:
* Acquire a multi-thread lock. If the lock has not already been
* allocated, do so, but that is an internal CRT error, since all locks
* should be allocated before first being acquired, either in
* _mtinitlocks or individually in _mtinitlocknum.
*
* Note that it is legal for a thread to aquire _EXIT_LOCK1
* multiple times.
*
*Entry:
* locknum = number of the lock to aquire
*
*Exit:
*
*Exceptions:
* A failure to allocate a new lock results in a fatal _RT_LOCK error.
*
*******************************************************************************/void __cdecl _lock (int locknum)
{/** Create/open the lock, if necessary*/if ( _locktable[locknum].lock == NULL ) {if ( !_mtinitlocknum(locknum) )_amsg_exit( _RT_LOCK );}/** Enter the critical section.*/EnterCriticalSection( _locktable[locknum].lock );
}/***
* _unlock - Release multi-thread lock
*
*Purpose:
* Note that it is legal for a thread to aquire _EXIT_LOCK1
* multiple times.
*
*Entry:
* locknum = number of the lock to release
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/void __cdecl _unlock (int locknum)
{/** leave the critical section.*/LeaveCriticalSection( _locktable[locknum].lock );
}
#define _mlock(l) _lock(l)
在Mtdll.h文件中定义了 访问每个临界区对象对应的宏
#define _SIGNAL_LOCK 0 /* lock for signal() */
#define _IOB_SCAN_LOCK 1 /* _iob[] table lock */
#define _TMPNAM_LOCK 2 /* lock global tempnam variables */
#define _CONIO_LOCK 3 /* lock for conio routines */
#define _HEAP_LOCK 4 /* lock for heap allocator routines */
#define _UNDNAME_LOCK 5 /* lock for unDName() routine */
#define _TIME_LOCK 6 /* lock for time functions */
#define _ENV_LOCK 7 /* lock for environment variables */
#define _EXIT_LOCK1 8 /* lock #1 for exit code */
#define _POPEN_LOCK 9 /* lock for _popen/_pclose database */
#define _LOCKTAB_LOCK 10 /* lock to protect semaphore lock table */
#define _OSFHND_LOCK 11 /* lock to protect _osfhnd array */
#define _SETLOCALE_LOCK 12 /* lock for locale handles, etc. */
#define _MB_CP_LOCK 13 /* lock for multibyte code page */
#define _TYPEINFO_LOCK 14 /* lock for type_info access */
#define _DEBUG_LOCK 15 /* lock for debug global structs */#define _STREAM_LOCKS 16 /* Table of stream locks */
这些临界区对象,为C运行库函数的多线程支持提供了,防止代码重入对一些静态及全家数据的访问的破坏。
再往下是
if ( (__flsindex = FLS_ALLOC(&_freefls)) == FLS_OUT_OF_INDEXES ) {
_mtterm();
return FALSE; /* fail to load DLL */
}
这里FLS_ALLOC就是前面提到的宏 在32位下 调用全家函数指针gpFlsAlloc 在64位下调用FlsAlloc, 这个函数在MSDN中是这样说明的
DWORD WINAPI FlsAlloc(__in PFLS_CALLBACK_FUNCTION lpCallback );
- lpCallback [in]
-
A pointer to the application-defined callback function of type PFLS_CALLBACK_FUNCTION. This parameter is optional. For more information, seeFlsCallback.
这里_freefls就是一个回调函数指针,这个回调函数在FlsFree相应的索引标记时被调用
_CRTIMP void
WINAPI
_freefls (void *data){_ptiddata ptd;pthreadlocinfo ptloci;pthreadmbcinfo ptmbci;/** Free up the _tiddata structure & its malloc-ed buffers.*/ptd = data;if (ptd != NULL) {if(ptd->_errmsg)_free_crt((void *)ptd->_errmsg);if(ptd->_namebuf0)_free_crt((void *)ptd->_namebuf0);if(ptd->_namebuf1)_free_crt((void *)ptd->_namebuf1);if(ptd->_asctimebuf)_free_crt((void *)ptd->_asctimebuf);if(ptd->_wasctimebuf)_free_crt((void *)ptd->_wasctimebuf);if(ptd->_gmtimebuf)_free_crt((void *)ptd->_gmtimebuf);if(ptd->_cvtbuf)_free_crt((void *)ptd->_cvtbuf);if (ptd->_pxcptacttab != _XcptActTab)_free_crt((void *)ptd->_pxcptacttab);_mlock(_MB_CP_LOCK);__try {if ( ((ptmbci = ptd->ptmbcinfo) != NULL) &&(InterlockedDecrement(&(ptmbci->refcount)) == 0) &&(ptmbci != &__initialmbcinfo) )_free_crt(ptmbci);}__finally {_munlock(_MB_CP_LOCK);}_mlock(_SETLOCALE_LOCK);__try {if ( (ptloci = ptd->ptlocinfo) != NULL ){__removelocaleref(ptloci);if ( (ptloci != __ptlocinfo) &&(ptloci != &__initiallocinfo) &&(ptloci->refcount == 0) )__freetlocinfo(ptloci);}}__finally {_munlock(_SETLOCALE_LOCK);}_free_crt((void *)ptd);}return;
看以看到 这里_freefls 用来清理释放为线程分配的_tiddate结构。
__flsindex = FLS_ALLOC(&_freefls)) == FLS_OUT_OF_INDEXES
这里FLS_OUT_OF_INDEXES 是一个宏标识 没有分配到索引号,而__flsindex 是一个全局变量来表示FlsAlloc分配的索引号,每个线程的_tiddate结构,在堆中分配内存,然后初始化后通过/!FLS_SETVALUE(__flsindex, (LPVOID)ptd) )将结构体指针保存到 局部储存中去。
再往下是:
if ( ((ptd = _calloc_crt(1, sizeof(struct _tiddata))) == NULL) ||
!FLS_SETVALUE(__flsindex, (LPVOID)ptd) )
{
_mtterm();
return FALSE; /* fail to load DLL */
}
_calloc_crt 这里为_tiddata结构在堆中分配内存,紧接着_initptd(ptd,NULL); 初始化分配到的结构体空间
/***
*void _initptd(_ptiddata ptd, pthreadlocinfo) - initialize a per-thread data structure
*
*Purpose:
* This routine handles all of the per-thread initialization
* which is common to _beginthread, _beginthreadex, _mtinit
* and _getptd.
*
*Entry:
* pointer to a per-thread data block
*
*Exit:
* the common fields in that block are initialized
*
*Exceptions:
*
*******************************************************************************/_CRTIMP void __cdecl _initptd (_ptiddata ptd,pthreadlocinfo ptloci)
{
#ifdef _M_IX86HINSTANCE hKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
#endif /* _M_IX86 */ptd->_pxcptacttab = (void *)_XcptActTab; //一个异常表,用于控制台信号实现ptd->_terrno = 0; //每个线程有自己的错误返回码 ptd->_holdrand = 1L;// It is necessary to always have GLOBAL_LOCALE_BIT set in perthread data// because when doing bitwise or, we won't get __UPDATE_LOCALE to work when// global per thread locale is set.ptd->_ownlocale = _GLOBAL_LOCALE_BIT;// Initialize _setloc_data. These are the only valuse that need to be// initialized.ptd->_setloc_data._cachein[0]='C';ptd->_setloc_data._cacheout[0]='C'; //本地化相关ptd->ptmbcinfo = &__initialmbcinfo; //字符相关_mlock(_MB_CP_LOCK);__try{InterlockedIncrement(&(ptd->ptmbcinfo->refcount));}__finally{_munlock(_MB_CP_LOCK);}// We need to make sure that ptd->ptlocinfo in never NULL, this saves us// perf counts when UPDATING locale._mlock(_SETLOCALE_LOCK);__try {ptd->ptlocinfo = ptloci;/** Note that and caller to _initptd could have passed __ptlocinfo, but* that will be a bug as between the call to _initptd and __addlocaleref* the global locale may have changed and ptloci may be pointing to invalid* memory. Thus if the wants to set the locale to global, NULL should* be passed.*/if (ptd->ptlocinfo == NULL)ptd->ptlocinfo = __ptlocinfo;__addlocaleref(ptd->ptlocinfo);}__finally {_munlock(_SETLOCALE_LOCK);}
}
最后
ptd->_tid = GetCurrentThreadId(); 设置线程ID
ptd->_thandle = (uintptr_t)(-1); 线程句柄设成-1
关于多线程更多参见http://blog.csdn.net/wangpengk7788/article/details/53938288
VS下EXE可执行文件启动代码剖析(2)_mtinit函数相关推荐
- Asp代码转换java代码器_asp下实现对HTML代码进行转换的函数
asp下实现对HTML代码进行转换的函数 更新时间:2007年08月08日 12:08:49 作者: '****************************** '函数:HTMLEncode( ...
- DirectX修复windows下.exe文件启动失败。
转载自http://blog.csdn.net/vbcom/article/details/7245186 DirectX修复工具最新版:DirectX Repair V3.5 增强版 NEW! 版 ...
- php读写分离数据不能同步,thinkphp 下数据库读写分离代码剖析
当采用原生态的sql语句进行写入操作的时候,要用execute,读操作要用query. MySQL数据主从同步还是要靠MySQL的机制来实现,所以这个时候MySQL主从同步的延迟问题是需要优化,延迟时 ...
- asp 转换html代码,asp下实现对HTML代码进行转换的函数
'****************************** '函数:HTMLEncode(reString) '参数:reString,待编码转换处理的字符串 '作者:阿里西西 '日期:2007/ ...
- windows下tomcat8启动脚本代码剖析--catalina.bat
Windows下,Tomcat可以以服务形式启动.停止,也可以执行脚本启动(startup.bat).停止(shutdown.bat).执行startup.bat时会调用catalina.bat,ca ...
- linux 批处理 exe文件内容,Linux_DOS批处理文件,DOS下的可执行文件有三种,分 - phpStudy...
DOS批处理文件 DOS下的可执行文件有三种,分别是EXE,COM和BAT.其中,EXE和COM文件都是二进制形式的,只有BAT文件是文本形式的,可以直接阅读.因 此,BAT文件和以上二进制可执行文件 ...
- Python写的代码打包成.exe可执行文件
Python写的代码打包成.exe可执行文件 1. 安装pyinstaller 2. [在线生成icon](http://www.ico51.cn/) 3. 打包命令 pyinstaller -i x ...
- 【Python学习笔记(二)】使用Pyinstaller将不同路径下的py文件打包成exe可执行文件
** 使用Pyinstaller将不同路径下的py文件打包成exe可执行文件 ** ** 前言 在Windows环境下需要打包一个python项目成exe可执行文件,共有一个主函数BomSoftwar ...
- 【转载】用cx_Freeze把Python代码打包成单个独立的exe可执行文件
链接:用cx_Freeze把Python代码打包成单个独立的exe可执行文件 [记录]用cx_Freeze把Python代码打包成单个独立的exe可执行文件 背景 之前已经折腾过: [记录]用PyIn ...
- 如何将Python写的代码打包成.exe可执行文件
有时候我们需要将自己写的代码打包成exe文件,给别人使用需要怎么办呢?以下将讲解Python代码如何打包成.exe文件. 1. 下载pyinstaller 因为Python中有很多三方包,我们想要这些 ...
最新文章
- 计算机基础知识第十讲,计算机文化基础(第十讲)学习笔记
- win下使用QT添加VTK插件实现点云可视化GUI
- SAP HUM 如何查询一个HU号码是否被软分配给了某个销售订单 ?
- 机器人也来玩“踢瓶盖挑战”了,你动他就动,靠脑电控制,路人也能玩丨MIT出品...
- base64/32/16编码
- React v16版本 源码解读
- 中文编程语言Z语言开源正式开源!!!
- goframe标签的一点说明
- edge如何导入html文件收藏夹,win10浏览器 edge浏览器收藏夹怎么导入?
- 靠谱测试人员需具备宏观把控能力
- [斯坦福]距离编码-更为强大的GNN
- 给Android SDK设置环境变量
- 惠普z800工作站bios设置_HP工作站 BIOS说明 适用Z228 Z440 Z230 Z640 Z840 Z800 Z620 Z420 Z820主板设置 -...
- ps——霓虹灯字体效果
- 小学生遭校长拳击内脏出血 求医救命钱遭抢(图)
- 用程序 揭秘 用手机号尾号暴露你年龄的 骗局
- centos 6.3_x64编译7.4 LFS
- 复旦大学管理学院2017年考博(高级微观经济学+管理理论综合)真题,高微老师上课资料
- Altium Designer布局布线时元器件移动
- HDUOJ 2048 - 神、上帝以及老天爷(错排公式)