代码改变世界

【简译】Windows 线程基础

2013-10-07 02:10  muzinian  阅读(478)  评论(0编辑  收藏  举报


翻译一篇关于windows线程的文章,原文在此。第一次翻译,如有错误请多指教

 

=========================================华丽的分割线============================================

介绍:

在现在的编程世界里,无论你使用的是java、.NET或C++,多线程编程已经成为编程语言不可缺少的一部分了。我要利用多线程的能力写出高响应和可扩展的应用程序。在使用.NET框架我遇到了各种像Task Parallel Library (TPL), Parallel LINQ (PLINQ), Task Factories, Thread Pool, Asynchronous programming modal等等为了并行任务处理的 Framework Class Libraries (FCL)。他们幕后使用的是Windows threads的力量达到并行。理解Windows threads的节本结构有助于实现和理解像TPL、PLINQ等的高级功能。本文将帮助你理解操作系统是如何实现线程的。

Windows threads包括什么:

Windows thread包括了三个基本组件:

1)Thread Kernel Object

2) Stack

3) TEB

这三个组合在一起构成了Windows thread。在分别解释这三个之前先做一个关于Windows kernel 和 kernel objects简明的介绍,他俩是Windows操作系统中最重要的一部分。

什么是操作系统内核:

在任何一个操作系统中,内核是最主要的部件。在应用程序与硬件的交互中,内核提供个一个抽象层。

内核是操作系统在加载中先启动的那一部分,并且一直驻留在物理内存中。它主要功能是管理硬件资并允许其他程序使用这些资源。浏览下列网站以了解更多:

http://en.wikipedia.org/wiki/Kernel_(computing)

什么是内核对象

内核需要维护像进程、线程、文件等众多资源的大部分数据,因此内核使用了"内核数据结构"也叫做内核对象。每个内核对象都是由内核分配并且只能由内核访问的一个内存块。这个内存块是一个数据结构,而这个数据结构的成员维护着这个对象的信息。在所有的对象类型中有些成员(安全描述符,使用计数等)是一样,但大多数数据成员是针对内核对象类型的。内核生成和维护几种内核对象类型,例如rocess objects, thread objects, event objects, file objects, file-mapping objects, I/O completion port objects, job objects, mutex objects, pipe objects, semaphore objects等。使用WinObj了解所有的内核对象类型。

http://technet.microsoft.com/en-us/sysinternals/bb896657.aspx

线程内核对象

每个线程生成一个线程内核对象,它是Windows thread最基本的组件。操作系统使用线程内核对象操作和执行线程,同时它保存了关于线程的统计信息。

线程上下文

每个线程内核对象都包括一组称为线程上下文的CPU 寄存器。上下文对应着线程最后一次执行时的线程的CPU寄存器的状态。线程的这组CPU寄存器被保存在一个CONTEXT结构中。在线程上下文中指令指针和栈指针寄存器是两个最重要的寄存器。栈指针存储着线程内部当前正在执行的函数的栈帧的起始地址。指令指针指向的是下一个要CPU执行的指令。操作系统在切换线程上下文时使用内核对象上下文信息。上下文切换是为了保证执行体可以重新在晚些时候同一点开始的一个存储和恢复线程状态的方法。下表显示了一些存储在线程内核对象关于线程的其他重要信息。

Property Name Description
CreateTime This field contains the time when the Thread was created.
ThreadsProcess This field contains a pointer to the EPROCESS Structure of the Process that owns this Thread.
StackBase This field contains the Base Address of this Thread’s Stack.
StackLimit This field contains the end of the Kernel-Mode Stack of the Thread.
TEB This field contains a pointer to the Thread’s Environment Block.
State This field contains the Thread’s current state.
Priority This field contains the Thread’s current priority.
ContextSwitches This field counts the number of Context Switches that the Thread has gone through (switching Contexts/Threads).
WaitTime This field contains the time until a Wait will expire.
Queue This field contains a Queue for this Thread.
Preempted This field specifies if the Thread will be preempted or not.
Affinity This field contains the Thread’s Kernel Affinity.
KernelTime This field contains the time that the Thread has spent in Kernel Mode.
UserTime This field contains the time that the Thread has spent in User Mode.
ImpersonationInfo This field contains a pointer to a structure used when the Thread is impersonating another one.
SuspendCount This field contains a count on how many times the Thread has been suspended.

一旦线程内核对象被创建,系统会分配内存给他用作线程栈。每个线程有自己的栈,他们用来管理在线程中函数内部变量和传递实参给正在执行的函数。当函数执行时,它会添加一些像实参和局部变量的状态数据到栈顶,而当函数退出时,他负责从栈中删除这些数据。除此之外,线程栈还可以存储函数调用的位置为了允许return语句返回当前位置。

 

每个线程有两个线程栈:用户模式栈和内核模式栈。

用户模式栈

用户模式栈用来存储局部变量和传递给方法的实参。它也存储表明了当当前方法返回时线程下一个要执行的方法的地址。默认Windows分配1MB给用户模式栈。

内核模式栈

当应用程序代码传递实参给内核函数时使用内核模式栈。为了安全,Windows复制每个从线程的用户模式栈到内核模式栈通过用户模式代码传递给内核的实参。一旦拷贝完成,内核可以验证实参值,同时因为应用程序不可以访问内核模式栈,实参值被验证后应用程序便不能修改,OS内核代码就开始操作他们了。另外,内核调用自身方法和内核模式栈传递自己的实参,存储函数局部变量和返回值。32位系统的内核模式栈是12KB而64位系统是24KB(均为Windows)。

通过以下链接了解更多:

http://www.linfo.org/kernel_space.html

http://en.wikipedia.org/wiki/Stack-based_memory_allocation 

http://en.wikipedia.org/wiki/Call_stack

 

Thread environment block (TEB)

TEB是一块在用户模式下分配和初始化的内存。TEB包括内存的一页(在x86和x64的CPU下是4KB)

 

TEB包含的一个重要的信息是关于被SEH(Microsoft Structured Exception Handling)使用的异常处理信息。TEB存储着线程的异常处理链的头部。每个线程进入的try块在这个链表头插入一个节点。当线程退出时从链表中删除这个节点。通过下面这个链接了解更多关于SEH的信息:

http://www.microsoft.com/msj/0197/Exception/Exception.aspx

另外,TEB包括thread-local storage data。在多线程应用程序中,常会出现维护一个线程独有的数据的寻求。这个线程指定数据存储的地方叫thread-local storage。通过下面这个链接了解更多关于thread-local storage信息:、

http://msdn.microsoft.com/en-us/library/windows/desktop/ms686749(v=vs.85).aspx

 

TEB的一些重要属性

 

Property Name Description
ThreadLocalStorage This field contains the thread specific data.
ExceptionList This field contains the Exception Handlers List used by SEH
ExceptionCode This field contains the last exception code generated by the Thread.
LastErrorValue This field contains the last DLL Error Value for the Thread.
CountOwnedCriticalSections This field counts the number of Critical Sections (a Synchronization mechanism) that the Thread owns.
IsImpersonating This field is a flag on whether the Thread is doing any impersonation.
ImpersonationLocale This field contains the locale ID that the Thread is impersonating.

 OS如何运行threads

OS在线程内核对象中保存所有为了线程调度或执行需要的信息。除了这些OS还在线程内核对象中存储了线程栈和线程TEB的地址。

OS在一个双向链表里维护着所有的内核对象。

OS重复上述行为从系统启动到系统结束。

线程内核对象维护着线程上下文结构,而这个结构对应着线程最后一次执行的CPU寄存器状态。每隔大约20ms,OS线程调度器查询当前在双向链表的线程内核对象。线程调度器选择一个可调度的的线程内核对象并加载存储在它的线程上下文的CPU寄存器的值。这个行为叫做上下文切换。此时,线程执行代码和操作在他自己的地址空间的数据。再过大约20ms后,调度器保存CPU寄存器值到线程上下文。调度器再一次检查剩下的可调度的线程内核对象,并选择一个加载它的上下文到CPU寄存器中继续执行。

OS重复上述行为从系统启动到系统结束。

所有的线程都不是可调度的

系统调度只是可调度的线程,在系统中大多数线程不是可调度的。例如,有些线程对象可能有一个suspend count大于0.这意味着这个线程是暂停的并且不应该被在然和CPU时间内被调度。除了暂停线程,许多其他线程不是可调度的因为它们正在等待一些事情的发生,像有些线程可能在等待一些锁得到释放。系统不会分配CPU时间给那些什么也不做的线程。如果机器有多个CPU,OS的为公平加载线程到CPU的算法更复杂。Windows可以在同步的每个CPU上调度不同的线程使得多线程实现真正的并发。在这种类型的系统上Windows内核操作所有的管理器和线程的调度。在你的代码里你不需要做任何特定的事情去获得多处理器提供的优势,你要做的是在你的代码里更好的利用这些CPU

进程和线程

(主要讲了进程和线程之间的关系,就不翻译了)

引用