线程及其实现.ppt
2.4 线程及其实现,2.4.1 引入多线程技术的动机,考察一个文件服务器的例子单线程(结构)进程(Single Threaded Process)多线程(结构)进程(Multiple Threaded process),单线程结构进程给并发程序设计效率带来问题,进程切换开销大进程通信代价大进程之间的并发性粒度较粗,并 发度不高不适合并行计算和分布并行计算 的要求不适合客户/服务器计算的要求。,线程的概念(1),操作系统中引入进程的目的是为了使多个程序并发执行,以改善资源使用率和提高系统效率,操作系统中再引入线程,则是为了减少程序并发执行时所付出的时空开销,使得并发粒度更细、并发性更好。,线程的概念(2),解决问题的基本思路:把进程的两项功能“独立分配资源”与“被调度分派执行”分离开来,进程作为系统资源分配和保护的独立单位,不需要频繁地切换;线程作为系统调度和分派的基本单位,能轻装运行,会被频繁地调度和切换,在这种指导思想下,产生了线程的概念。,2.4.2 多线程环境中的进程与线程,单线程进程的内存布局和运行,管理和执行相分离的进程模型,多线程进程的内存布局,多线程环境中进程的定义,进程是操作系统中进行保护和资源分配的基本单位。它具有:一个虚拟地址空间,用来容纳进程的映像;对处理器、其他(通信的)进程、文件和I/O资源等的存取保护机制。,多线程环境中的线程概念,线程是操作系统进程中能够独立执行的实体(控制流),是处理器调度和分派的基本单位。线程是进程的组成部分,每个进程内允许包含多个并发执行的实体(控制流),这就是多线程。,线程主要组成,线程执行状态(运行、就绪、);当线程不运行时,有一个受保护的线程上下文,用于存储现场信息。所以,线程也可被看作是执行在进程内的一个独立的程序计数器;一个执行堆栈 一个容纳局部变量的主存存储区。,线程具有以下特性,并行性:共享性:动态性:结构性:,线程的内存布局,线程又称轻量进程,线程运行在进程的上下文中,并使用进程的资源和环境。系统调度的基本单位是线程而不是进程,每当创建一个进程时,至少要同时为该进程创建一个线程,否则该进程无法被调度执行。,线程的状态(1),线程状态有:运行、就绪和阻塞,线程的状态转换也类似于进程。挂起状态对线程是没有意义的,如果进程挂起后被对换出主存,则它的所有线程因共享了进程的地址空间,也必须全部对换出去。,线程的状态(2),处于运行态的线程阻塞时,对某些线程实现机制,所在进程也转换为阻塞态,即使这个进程存在另一个处于就绪态的线程;对另一些线程实现机制,如果存在另外一个处于就绪态的线程,则调度该线程处于运行状态,否则进程才转换为阻塞态。,线程管理和线程库(1),多线程技术利用线程包(库)提供线程原语集来支持多线程运行,有的操作系统直接支持多线程,而有的操作系统不支持多线程。线程包(库)可分成两种:用户空间中运行的线程包(库)和内核中运行的线程包(库)。,线程管理和线程库(2),线程包(库)提供一组API,支持应用程序创建、调度、撤销和管理线程的运行。基本线程控制原语:孵化(Spawn):又称创建线程。封锁(Block):又称阻塞线程。活化(Unblock):又称恢复线程。结束(Finish):又称撤销线程。,并发多线程程序设计的优点,快速线程切换。减少(系统)管理开销。(线程)通信易于实现。(线程)通信易于实现。并行程度提高。节省内存空间。,多线程技术的应用(1),进程中线程多种组织方式:第一种是调度员工作者模式 第二种是组模式 第三种是流水线模式,多线程技术的应用(2),前台和后台工作。C/S应用模式。异步处理。加快执行速度。设计用户接口。,2.4.3 线程的实现,从实现的角度看,线程可以分成:用户级线程ULT(如Java,Informix)内核级线程KLT(如OS/2)。混合式线程(如,Solaris)。,各种线程实现方法,1.内核级线程(1),纯内核级线程设施中,线程管理的所有工作由操作系统内核做。内核专门提供KLT API,应用程序区不需要有线程管理代码。Windows NT 和 OS/2都是采用这种方法的例子。,内核级线程(2),线程执行中可通过内核创建线程原语来创建其他线程,这个应用的所有线程均在一个进程中获得支持。内核要为整个进程及进程中的单个线程维护现场信息,应在内核中建立和维护PCB及TCB,内核的调度是在线程的基础上进行的。,内核级线程主要优点,多处理器上,内核能同时调度同一进程中多个线程并行执行。进程中的一个线程被阻塞了,内核能调度同一进程的其它线程占有处理器运行。内核线程数据结构和堆栈很小,KLT切换快,内核自身也可用多线程技术实现,能提高系统的执行速度和效率。,内核级线程的主要缺点,应用程序线程在用户态运行,而线程调度和管理在内核实现,在同一进程中,控制权从一个线程传送到另一个线程时需要用户态-内核态-用户态的模式切换,系统开销较大。,2.用户级线程,纯ULT设施中,线程管理工作由应用程序做,内核不知道线程的存在。任何应用程序均需通过线程库进行程序设计,再与线程库连接后运行来实现多线程。线程库是一个ULT管理的例行程序包,实质上线程库是线程的运行支撑环境。,ULT线程“孵化”过程,进程开始只有一个线程,它可以孵化新线程,通过过程调用把控制权传送给“孵化”过程,由线程库为新线程创建一个TCB,并置为就绪态,按一定的调度算法把控制权传递给进程中处于就绪态的一个线程。当控制权传送到线程库时,当前线程的现场信息应被保存,而当线程库调度一个线程执行时,要恢复它的现场信息。,线程调度和进程调度间的关系(1),假设进程B正在执行线程3,可能出现下列情况:进程B的线程3发出一个封锁B的系统调用(如I/O操作),通知内核进行I/O并将进程B置为等待状态,按照由线程库所维护的数据结构,进程B的线程3仍处在运行态。线程3并不实际地在一个处理器上运行,而是可理解为在线程库的运行态中。这时,进程B为等待态,线程3为线程库运行态。,线程调度和进程调度间的关系(2),一个时钟中断传送控制给内核,内核中止当前时间片用完的进程B,并把它放入就绪队列,切换到另一个就绪进程,此时,按由线程库维护的数据结构,进程B的线程3仍处于运行态。这时,进程B己处于就绪态,但线程为线程库运行态。两种情况中,当内核切换控制权返回到进程B时,便恢复执行线程3。,ULT优点,线程切换不需要内核特权方式,按应用特定需要来调度,ULT能运行在任何OS上,,ULT的缺点,线程执行系统调用时,不仅该线程被阻塞,且进程内的所有线程会被阻塞。纯ULT中,多线程应用不能利用多重处理的优点。内核在一段时间里,分配一个进程仅占用一个CPU,进程中仅有一个线程能执行。,克服上述问题的方法,第一种方法是用多进程并发程序设计代替多线程并发程序设计,这种方法事实上放弃了多线程带来的所有优点。第二种方法是采用jacketing技术解决阻塞线程的问题。,3 混合式线程,混合系统中,内核支持KLT多线程的建立、调度和管理,也提供线程库,允许应用程序建立、调度和管理ULT。应用程序的多个ULT映射成一些KLT,程序员可按应用需要和机器配置调整KLT数目,以达到较好效果。一个应用中的多个线程能同时在多处理器上并行运行,且阻塞一个线程时并不需要封锁整个进程。,2.4.4 实例研究:Solaris的进程与线程,Solaris中的进程与线程概念 进程(Process):用户级线程(User-Level Threads):轻量进程(Light Weight Process):内核级线程(Kernel-Level Threads):,Solaris的线程实现(1),用户层和核心层,用户层在用户线程库中实现;核心层在操作系统内核中实现。ULT是一个代表应用线程的数据结构,纯用户级概念,占用用户空间资源,对核心是透明的。ULT和KLT不一一对应,通过轻量级进程LWP来映射两者之间的联系。,Solaris的线程实现(2),进程可以设计为一个或多个LWP,一个LWP上又可以开发多个ULT,LWP与ULT一样共享进程的资源。KLT和LWP是一一对应的,一个ULT要通过核心KLT和LWP二级调度后才真正占有处理器运行。,Solaris的线程实现(3),有了LWP,可在用户级实现ULT,每个进程可以创建几十个ULT,而又不占用核心资源。由于ULT共享用户空间,当LWP在一个进程的不同ULT间切换时,仅是数据结构的切换,其时间开销远低于两个KLT间的切换时间。,Solaris线程的使用(1),Solaris线程的使用(2),进程1是传统的单线程进程,进程2是一个纯的ULT应用,进程3是多线程与多LWP的对应,进程4的线程与LWP是一对一地捆绑的,进程5包括多ULT映射到多LWP上,以及ULT与LWP的一一捆绑,并且还有一个LWP捆在单个处理器上。,Solaris线程的使用(3),Solaris已经有进程、KLT和ULT,为什么还要引入LWP?原因是各种应用需求,有些应用逻辑并行性程度高,有些应用物理并行性要求高。例1:窗口系统是典型的逻辑并行性程度高的应用。例2:大规模并行计算是物理并行性要求高的应用。,Solaris中进程结构,LWP的数据结构(1),轻量进程标识符标识了轻量进程 优先数定义了轻量进程执行的优先数 信号掩码定义了内核能够接受的信号 寄存器域用于存放轻量进程让出处理器时的现场信息 轻量进程的内核栈包括每个调用层次的系统调用的参数、返回值和出错码,LWP的数据结构(2),交替的信号堆栈 用户或用户和系统共同的虚时间警示 用户时间和系统处理器使用 资源使用和预定义数据 指向对应内核线程的指针 指向进程结构的指针,ULT的数据结构,线程标识符标识了线程优先数定义了线程执行的优先数信号掩码定义了能够接受的信号 寄存器域用于存放线程让出处理器时的现场信息堆栈用于存放线程运行数据线程局部存储器用于存放线程局部数据,KLT的数据结构(1),内核寄存器数据保存区 优先级和调度信息 KLT的队列指针和堆栈指针 相关LWP的指针及信息 进程数据结构,包括:与进程相关的KLT 进程地址空间指针 用户权限表 信号处理程序清单 与用户执行有关信息,KLT的数据结构(2),Solaris支持实时类进程,内核是可抢占的,内核函数和过程全部用线程实现,Solaris的内核是KLT的集合。KLT分两种,一种是负责执行一个指定的内核函数;另一种用来支持和运行LWP,KLT是独立被调度和分配到CPU上去执行。,Solaris的线程状态(1),Solaris的线程状态(2),考虑非捆绑线程(多个ULT共享LWP),线程可能处于四个状态之一:可运行、活跃、睡眠和停止。处在活跃状态的一个ULT是指目前分配到LWP上,并且在对应的内核线程KLT执行时,它被执行。出现事件会导致ULT离开活跃态,一个活跃的ULT正在执行,下面的事件可能发生:,Solaris的线程状态(3),同步:挂起:剥夺:让位:,2.4.5 实例研究:Windows 2000/XP的进程与线程,三个层次执行对象:作业是共享一组配额限制和安全性限制的进程的集合;进程是相应于一个应用的实体,它拥有自己的资源,如主存,打开的文件;线程是可被内核调度的执行实体,它可以被中断,使CPU能转向另一线程执行。,面向对象(object oriented)概念,对象 属性和方法对象类 实例 消息 封装性 继承性(子类,超类),Windows 2000/XP对象分类(1),(1)执行体对象 由执行体的组件实现的对象,用来实现各种外部功能,用户态程序(服务器对象)可访问执行体对象。执行体创建的对象可进一步分类:事件对象、互斥对象、信号量对象、文件对象、文件映射对象、进程对象、线程对象和管道对象等。,Windows 2000/XP对象分类(2),(2)内核对象 内核实现的更原始的对象集合,包括:内核过程对象、异步过程调用对象、延迟过程调用对象、中断对象、电源通知对象、电源状态对象、调度程序对象等。内核对象对用户态代码是不可见的,仅在执行体内创建和使用,许多执行体对象包含一个或多个内核对象,而内核对象能提供仅能由内核来完成的基本功能。,Windows 2000/XP中对象结构,对象头对象体 对象管理器,进程及控制和使用的资源(1),对象和句柄间的关系,进程及控制和使用的资源(2),用户注册时,为用户建立一个访问令牌Access Token,包括安全标识和进程凭证。用户建立的进程均有这个访问令牌的拷贝。内核使用它验证用户是否具有存取安全对象或安全对象上执行受限功能的能力。当前分配给这个进程的虚拟地址空间块,进程不能直接改它,必须依靠为进程提供内存分配服务的虚存管理例程。,进程及控制和使用的资源(3),进程有一个对象表,其中包括进程与其使用资源之间的联系,通过对象句柄便可对某资源进行引用,存取令牌控制进程是否能改变其自身属性。,进程对象(1),进程由对象表示。当接受到适当消息时,进程就执行一个服务,只能通过传递消息给提供服务的进程对象来调用服务。使用进程对象类建立一个新进程,对象类被定义作为一个能够生成新的对象实例的模板,并且在建立对象实例时,属性将被赋值。,进程对象(2),进程由一个执行体进程块表示。它包括进程的属性,指向其它相关的属性,如进程都有一个或多个执行体线程(ETHREAD)块表示的线程。除了进程环境块(PEB)存在于进程地址空间中外,EPROCESS块及其相关的其他数据结构存在于系统空间中。,执行体进程(EPROCESS)块,调用CreateProcess函数创建WIN32进程(1),创建WIN32进程的具体步骤:打开将在进程中被执行的映像文件(.EXE)。创建Windows2000/XP执行体进程对象。创建初始线程(堆栈、描述表、执行体线程对象)。,调用CreateProcess函数创建WIN32进程(2),通知WIN32子系统已创建了一个新的进程,以便它可设置新的进程和线程。启动初始线程的执行(除非指定了CREATE_SUSPENDED标志)。在新进程和线程的描述表中,完成地址空间的初始化,加载所需的DLL,并开始程序的执行,线程对象(1),ETHRED块中有关项目的内容:创建和退出时间:进程识别信息:线程启动地址:LPC消息信息:挂起的I/O请求:,线程对象(2),调度程序头信息:执行时间:内核堆栈信息指引元:系统服务表指引元:调度信息:,ETHRED块的结构,用于线程的WIN32函数(1),CreateThread:创建新线程。CreateRemoteThread:在另一个进程创建线程。ExitThread:退出当前线程。TerminateThread:终止线程。GetExitCodeThread:返回另一个线程的退出代码。,用于线程的WIN32函数(2),GetThreadTimes:返回另一个线程的定时信息。GetThreadSelectorEntry:返回另一个线程的描述符表入口。GetThreadContext:返回线程的CPU寄存器。SetThreadContext:更改线程的CPU寄存器。,调用CreateThread函数创建WIN32线程步骤,在进程地址空间为线程创建用户态堆栈。初始化线程的硬件描述表。调用NtCreateThread创建处于挂起状态的执行体线程对象。通知WIN32子系统已创建了一个新线程。线程句柄和ID被返回到调用程序。除非调用程序用CREATE_SUSPEND标识设置创建线程,否则线程将被恢复以便调度执行。,Windows2000/XP线程状态,作业对象(1),作业对象是可命名、保护、共享的对象,能够控制与作业有关的进程属性。作业对象的基本功能是允许系统将进程组看作是一个单元,对其进行管理和操作。作业对象也为所有与作业有关的进程和所有与作业有关但已被终止的进程记录基本的账号信息。,作业对象(2),作业对象包含一些对每一个与该作业有关的进程的强制限制用户也能够在作业中的进程上设置安全性限制用户也能够在作业中的进程上设置用户接口限制,作业对象(3),进程只能属于一个作业,一旦进程建立,它与作业的联系便不能中断;所有由进程创建的进程和它们的后代也和同样的作业相联系。在作业对象上的操作会影响与作业对象相联系的所有进程。,作业对象(4),作业对象的WIN32函数包括:CreateJobObject:创建作业对象。Open JobObject:通过名称打开现有的作业对象。AssignProcessToJobObject:添加一个进程到作业。,作业对象(5),TerminateJobObject:终止作业中的所有进程。SetInformationToJobObject:设置限制。QueryInformationToJobObject:获取有关作业的信息,如:CPU时间、页错误技术、进程的数目、进程ID列表、配额或限制、安全限制等。,