快乐虾
http://blog.csdn.net/lights_joy/
lights@hb165.com
本文适用于
Cygwin checkout-2008-09-28
vs2008
欢迎转载,但请保留作者信息
1.1 文档的说法
cygwin为了支持多线程,使用了tls,但是它所实现的TLS和windows系统提供的TLS有所区别,cygwin没有使用windows中TLS相关的API。
关于这一部分的实现,其文档是这样解释的:
All cygwin threads have separate context in an object of class _cygtls. The
storage for this object is kept on the stack in the bottom CYGTLS_PADSIZE
bytes. Each thread references the storage via the Thread Environment Block
(aka Thread Information Block), which Windows maintains for each user thread
in the system, with the address in the FS segment register. The memory
is laid out as in the NT_TIB structure from <w32api/winnt.h>:
typedef struct _NT_TIB {
struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
PVOID StackBase;
PVOID StackLimit;
PVOID SubSystemTib;
_ANONYMOUS_UNION union {
PVOID FiberData;
DWORD Version;
} DUMMYUNIONNAME;
PVOID ArbitraryUserPointer;
struct _NT_TIB *Self;
} NT_TIB,*PNT_TIB;
Cygwin sees it like this:
extern exception_list *_except_list asm ("%fs:0"); // exceptions.cc
extern char *_tlsbase __asm__ ("%fs:4"); // cygtls.h
extern char *_tlstop __asm__ ("%fs:8"); // cygtls.h
And accesses cygtls like this:
#define _my_tls (((_cygtls *) _tlsbase)[-1]) // cygtls.h
Initialization always goes through _cygtls::init_thread(). It works
in the following ways:
* In the main thread, _dll_crt0() provides CYGTLS_PADSIZE bytes on the stack
and passes them to initialize_main_tls(), which calls _cygtls::init_thread().
It then calls dll_crt0_1(), which terminates with cygwin_exit() rather than
by returning, so the storage never goes out of scope.
If you load cygwin1.dll dynamically from a non-cygwin application, it is
vital that the bottom CYGTLS_PADSIZE bytes of the stack are not in use
before you call cygwin_dll_init(). See winsup/testsuite/cygload for
more information.
* Threads other than the main thread receive DLL_THREAD_ATTACH messages
to dll_entry() (in init.cc).
- dll_entry() calls munge_threadfunc(), which grabs the function pointer
for the thread from the stack frame and substitutes threadfunc_fe(),
- which then passes the original function pointer to _cygtls::call(),
- which then allocates CYGTLS_PADSIZE bytes on the stack and hands them
to call2(),
- which allocates an exception_list object on the stack and hands it to
init_exceptions() (in exceptions.cc), which attaches it to the end of
the list of exception handlers, changing _except_list (aka
tib->ExceptionList), then passes the cygtls storage to init_thread().
call2() calls ExitThread() instead of returning, so the storage never
goes out of scope.
Note that the padding isn't necessarily going to be just where the _cygtls
structure lives; it just makes sure there's enough room on the stack when the
CYGTLS_PADSIZE bytes down from there are overwritten.
所有依赖于tls的全局变量都必须从_tlsbase这个指针开始计算,它的定义在cygtls.h中:
extern char *_tlsbase __asm__ ("%fs:4");
但在vs2008下并不支持这样的定义,为此不得不另寻它法。
1.2 _tlsbase
在windows的线程中,可以访问一个叫NT_TIB的结构体以达到类似的目的,在WinNT.h(7220)中定义了这个结构体:
typedef struct _NT_TIB {
struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
PVOID StackBase;
PVOID StackLimit;
PVOID SubSystemTib;
union {
PVOID FiberData;
DWORD Version;
};
PVOID ArbitraryUserPointer;
struct _NT_TIB *Self;
} NT_TIB;
typedef NT_TIB *PNT_TIB;
fs这个寄存器保存指向存有NT_TIB结构体的段,因此,我们通过适当转换对cygwin的定义进行改写:
CONTEXT context;
LDT_ENTRY ldt;
HANDLE hThread = ::GetCurrentThread();
memset(&ldt, 0, sizeof(LDT_ENTRY));
memset(&context, 0, sizeof(CONTEXT));
context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
GetThreadContext(hThread,&context);
GetThreadSelectorEntry(hThread, context.SegFs, &ldt);
DWORD dwFSBase = ( ldt.HighWord.Bits.BaseHi << 24) |
(ldt.HighWord.Bits.BaseMid << 16) |
ldt.BaseLow;
NT_TIB tib;
memset(&tib, 0, sizeof(NT_TIB));
DWORD dwBytes;
HANDLE hProcess = ::GetCurrentProcess();
ReadProcessMemory( hProcess, (LPCVOID)dwFSBase, &tib, sizeof(NT_TIB), &dwBytes);
这段代码选取得fs这个段寄存器的值,再将之转换为线性地址,接着读出NT_TIB结构体的内容,在有了NT_TIB之后,直接取其StackBase就是cygwin里面的_tlsbase这个指针的值。
1.3 _my_tls
Cygwin使用了一个叫_cygtls的结构体来保存每个线程的内部数据,为了达到每个线程都有不同数据的目的,按照文档的说法,它直接将这个结构体放入每个线程栈的底部。
#define _my_tls (((_cygtls *) _tlsbase)[-1]) // cygtls.h
这样,通过_my_tls,每个线程就可以访问到自己的内部数据了。
但在实际的代码中,_my_tls是这样定义的:
#define _my_tls (*((_cygtls *) (_tlsbase - CYGTLS_PADSIZE)))
也就是说,它将从线程栈的中间取一段空间出来。
const int CYGTLS_PADSIZE = 18000; /* FIXME: Find some way to autogenerate
this value */
之所以要留这么一段空间,是因为在windows创建线程之时,虽然将ESP指向栈的底部,但是线程开始运行之后,必然会造成ESP指针向下增长,如果直接将_cygtls这个结构体放在栈的底部,必然造成某些成员被改写。
同样由于这个原因,cygwin要求在使用cywin.dll之前人为地将ESP向下增长,直到_my_tls之下,这样再使用栈的时候就不会覆盖_my_tls的内容了。
要达到这个目的也很简单,只要在调用cywin.dll之前定义一个简单的数组:
char padding[CYGTLS_PADSIZE];
这样栈指针自然就往下增长了!这个数组的大小只要比CYGTLS_PADSIZE大就可以了。当然,在程序里面不可往此数组写东西,否则就会覆盖_my_tls的内容!
在vs2008下使用cygwin(23):stdin,stdout和stderr(2008-10-21)
在vs2008下使用cygwin(22):使用tls(2008-10-20)
分享到:
相关推荐
自 2000 年以来,Cygwin 经历了多次重大版本更新,下面列举了部分关键版本及其主要改进: ##### 1.7.1 版本 3.0 更新日志 - 引入了新的多线程支持。 - 对内存管理和性能进行了优化。 - 增加了对最新 Windows 操作...
"cygwin" 和 "ndk" 标签则强调了编译过程中使用的工具和技术,Cygwin提供了Linux环境下的命令行工具和编译环境,而NDK则为构建原生库提供了必要的API和编译工具。 在压缩包内的文件名称 "OpenSSL1.0.1cForAndroid-...
标签 "tengine-2.3.2 nginx cygwin" 进一步指明了主要的技术元素:Tengine 的版本号,Nginx(因为 Tengine 是基于 Nginx 的),以及编译环境 Cygwin。 压缩包内的文件名列表揭示了 Tengine 运行所需的动态链接库...
- **概念**:对称算法是一种加密技术,在该技术中,加密和解密使用相同的密钥。 - **常见对称算法**:包括DES、3DES、AES、RC4等。 - **特点**: - 加密速度快。 - 密钥管理成为关键问题。 **1.2 摘要算法** - *...
- **安全协议分析**:用于分析各种安全协议(如TLS、SSH等)的安全性。 - **学术研究**:支持研究人员对新型加密协议进行安全性验证。 - **工业应用**:帮助企业确保其通信协议的安全性符合标准。 #### 三、...
这个主题涉及到了多种技术和工具,旨在确保不同操作系统之间的高效数据交换。以下是一些关于如何实现这一目标的关键知识点: 1. **FTP (File Transfer Protocol)**: FTP是最传统的文件传输协议,适用于在不同操作...
语音通信则是远程交互中的关键组成部分,它提供了即时、直观的交流方式。结合这些标签,我们可以深入探讨以下几个相关知识点: 1. **C++编程基础**:C++ 是一种面向对象的编程语言,支持类、继承、多态等特性,同时...
对于Windows平台,CyaSSL提供了专门的构建指南,通常会推荐使用特定的编译工具链,如MinGW、Cygwin或MSVC等。构建过程与*nix系统类似,但可能会有一些特定于Windows的配置选项。 ##### 2.4 在非标准环境下构建...
三、OpenSSL进阶:深入探索关键技术 ### 堆栈 OpenSSL中的堆栈是一种特殊的线性数据结构,用于存储一组同类型的对象。通过`sk_new_null()`、`sk_push()`、`sk_pop_free()`等函数,可以轻松地管理和操作堆栈中的...
Windows用户需要特别注意的是,haproxy在Windows上可能需要依赖cygwin或其他模拟Unix环境的工具,以运行其基于Unix的命令行工具。 配置haproxy的核心在于修改`haproxy.cfg`文件。这个配置文件包含了haproxy的所有...