`
xiaoer_1982
  • 浏览: 1865266 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

cygwin关键技术:tls

阅读更多

快乐虾

http://blog.csdn.net/lights_joy/

lights@hb165.com

本文适用于

Cygwin checkout-2008-09-28

vs2008

欢迎转载,但请保留作者信息

1.1 文档的说法

cygwin为了支持多线程,使用了tls,但是它所实现的TLSwindows系统提供的TLS有所区别,cygwin没有使用windowsTLS相关的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的内容!

1 参考资料

vs2008下使用cygwin23):stdinstdoutstderr(2008-10-21)

vs2008下使用cygwin22):使用tls(2008-10-20)

分享到:
评论

相关推荐

    Cygwin用户说明书. Cygwin User's Guide. cygwin-ug-net

    自 2000 年以来,Cygwin 经历了多次重大版本更新,下面列举了部分关键版本及其主要改进: ##### 1.7.1 版本 3.0 更新日志 - 引入了新的多线程支持。 - 对内存管理和性能进行了优化。 - 增加了对最新 Windows 操作...

    OpenSSL1.0.1cForAndroid.zip

    "cygwin" 和 "ndk" 标签则强调了编译过程中使用的工具和技术,Cygwin提供了Linux环境下的命令行工具和编译环境,而NDK则为构建原生库提供了必要的API和编译工具。 在压缩包内的文件名称 "OpenSSL1.0.1cForAndroid-...

    tengine-2.3.2_win64.rar

    标签 "tengine-2.3.2 nginx cygwin" 进一步指明了主要的技术元素:Tengine 的版本号,Nginx(因为 Tengine 是基于 Nginx 的),以及编译环境 Cygwin。 压缩包内的文件名列表揭示了 Tengine 运行所需的动态链接库...

    openssl编程帮助文档

    - **概念**:对称算法是一种加密技术,在该技术中,加密和解密使用相同的密钥。 - **常见对称算法**:包括DES、3DES、AES、RC4等。 - **特点**: - 加密速度快。 - 密钥管理成为关键问题。 **1.2 摘要算法** - *...

    ProVerif manual

    - **安全协议分析**:用于分析各种安全协议(如TLS、SSH等)的安全性。 - **学术研究**:支持研究人员对新型加密协议进行安全性验证。 - **工业应用**:帮助企业确保其通信协议的安全性符合标准。 #### 三、...

    教育科研-学习工具-Linux服务端与windows客户端之间跨平台文件的传输方法.zip

    这个主题涉及到了多种技术和工具,旨在确保不同操作系统之间的高效数据交换。以下是一些关于如何实现这一目标的关键知识点: 1. **FTP (File Transfer Protocol)**: FTP是最传统的文件传输协议,适用于在不同操作...

    C++ 远程控制 白板 语音 程序

    语音通信则是远程交互中的关键组成部分,它提供了即时、直观的交流方式。结合这些标签,我们可以深入探讨以下几个相关知识点: 1. **C++编程基础**:C++ 是一种面向对象的编程语言,支持类、继承、多态等特性,同时...

    cyassl手册

    对于Windows平台,CyaSSL提供了专门的构建指南,通常会推荐使用特定的编译工具链,如MinGW、Cygwin或MSVC等。构建过程与*nix系统类似,但可能会有一些特定于Windows的配置选项。 ##### 2.4 在非标准环境下构建...

    OPENSSL

    三、OpenSSL进阶:深入探索关键技术 ### 堆栈 OpenSSL中的堆栈是一种特殊的线性数据结构,用于存储一组同类型的对象。通过`sk_new_null()`、`sk_push()`、`sk_pop_free()`等函数,可以轻松地管理和操作堆栈中的...

    haproxy-2.1.0-windows.rar

    Windows用户需要特别注意的是,haproxy在Windows上可能需要依赖cygwin或其他模拟Unix环境的工具,以运行其基于Unix的命令行工具。 配置haproxy的核心在于修改`haproxy.cfg`文件。这个配置文件包含了haproxy的所有...

Global site tag (gtag.js) - Google Analytics