Getptd(转)

CreateThread导致内存泄露的原因

这得从C运行时库说起了。
VC运行时库,有一个宏errno,用来获得上一步操作的错误码,类似于Win32中的GetLastError()函数。在多线程环境下,不同线程调用errno返回的都是caller线程的错误码,绝对不会混淆,这是因为使用了TLS技术。
TLS,Thread Local Storage,是用来存取线程相关数据的一种技术,在Win32中由操作系统的Tls*系列函数提供支持。例如,可以在程序开始的地方调用TlsAlloc()函数,获得一个TLS index,这个index在进程范围内有效,然后可以创建n个线程,在每个线程中使用TlsSetValue(index,data)将线程相关数据和index关联起来,使用TlsGetValue(index)来获取当前线程和index相关联的的线程相关数据。
查看msdn可以发现,Tls*函数的定义如下:

[cpp] view plaincopy
DWORD WINAPI TlsAlloc(void);

BOOL WINAPI TlsSetValue(
__in DWORD dwTlsIndex,
__in LPVOID lpTlsValue
);

LPVOID WINAPI TlsGetValue(
__in DWORD dwTlsIndex
);

BOOL WINAPI TlsFree(
__in DWORD dwTlsIndex
);

观察TlsSetValue/TlsGetValue的原型可以发现,与index关联的数据只能是void *类型,因此通常的做法是在线程开始的时候,为这个线程分配一块内存,用于存储所有与线程相关的数据,然后把这块内存的起始地址与index关联起来。如果这块内存在线程退出的时候没有释放掉,那就有内存泄露的危险。

回到errno,来看看C运行时库是如何实现errno的。
errno的声明和实现如下:

[c-sharp] view plaincopy
/* error.h - errno的声明 */
_CRTIMP extern int * __cdecl _errno(void);

define errno (*_errno())

/* dosmap.c - errno的实现 */
int * __cdecl _errno(
void
)
{
_ptiddata ptd = _getptd_noexit();
if (!ptd) {
return &ErrnoNoMem;
} else {
return ( &ptd->_terrno );
}
}

观察_errno的代码,函数首先调用了_getptd_noexit()函数,这个函数的代码如下:

[cpp] view plaincopy
/* tiddata.c - _getptd_noexit()实现 */
_ptiddata __cdecl _getptd_noexit (
void
)
{
_ptiddata ptd;
DWORD TL_LastError;

TL_LastError = GetLastError();

ifdef _M_IX86

/*
* Initialize FlsGetValue function pointer in TLS by calling __set_flsgetvalue()
*/

if ( (ptd = (__set_flsgetvalue())(__flsindex)) == NULL ) {

else /* _M_IX86 */

if ( (ptd = FLS_GETVALUE(__flsindex)) == NULL ) {

endif /* _M_IX86 */

/*
* no per-thread data structure for this thread. try to create
* one.
*/

ifdef _DEBUG

extern void * __cdecl _calloc_dbg_impl(size_t, size_t, int, const char , int, int );
if ((ptd = _calloc_dbg_impl(1, sizeof(struct _tiddata), _CRT_BLOCK, FILE, LINE, NULL)) != NULL) {

else /* _DEBUG */

if ((ptd = _calloc_crt(1, sizeof(struct _tiddata))) != NULL) {

endif /* _DEBUG */

if (FLS_SETVALUE(__flsindex, (LPVOID)ptd) ) {

/*
* Initialize of per-thread data
*/

_initptd(ptd,NULL);

ptd->_tid = GetCurrentThreadId();
ptd->_thandle = (uintptr_t)(-1);
}
else {

/*
* Return NULL to indicate failure
*/

_free_crt(ptd);
ptd = NULL;
}
}
}

SetLastError(TL_LastError);

return(ptd);
}

_getptd_noexit()函数首先通过TLS查找线程相关数据,如果没有找到,就分配一块内存,存放_tiddata结构,并将这块内存与__flsindex相关联。由此可见,errno的确使用了TLS技术,而且通过查找_getptd_noexit() 可以发现,VC运行时库中很多很多函数都使用了TLS,errno只不过是其中的一个典型。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值