
3.4 DCE的线程
分布式系统的发展对分布式操作系统提出更高的要求,要求进程通信能提供更细粒度的并行功能,也称为轻型进程。本节介绍进程和线程的概念、线程调度和同步方式、DCE的多线程机制、结构和实现等。
3.4.1 进程和线程
1.进程和线程的概念
进程是指程序的执行,是程序在一个数据集合上运行的过程。线程是程序运行和执行调度的最小单位。线程是实体,可有自己的PC、堆栈和寄存器。它能生成线程,也有运行、就绪和结束等状态,同一个进程可有多个线程。线程的状态如表3-1所示。
表3-1 线程的状态

线程也称为轻型进程(Lightweight Process)。同一进程的诸线程间不是完全独立的,必须严格处于同一地址空间,它们能共享此进程的全局变量、打开文件、子进程、计时器、信号量和统计信息等。
由于多个线程同在一个地址空间,线程间的数据共享是很容易实现的,但要保证共享数据的完整性还要有一些控制机制。
DCE中使用了两种互斥变量。
(1)Mutex变量:用于在多个线程之间保证资源的完整性。Mutex变量有两种状态,即Locked和Unlocked状态。在访问一个共享资源,如调用一个全局变量之前,每个线程必须给相应的Mutex变量加锁,以阻止其他线程的访问,在完成对该共享资源的访问后再释放锁。
(2)条件变量:它允许一个线程暂时终止自己执行,直到另一个线程通知它共享数据满足某一特定的状态。
2.线程的特征
(1)线程是一个实体,是最小的基本执行单位。
(2)线程是程序运行和调度的基本单位(也称为轻型进程)。
(3)线程有独立的程序计数器、寄存器、堆栈。
(4)线程运行在Task(任务)环境(包括分页虚存的地址空间)下。
(5)线程可使用Task环境的所有资源。
(6)一个任务环境可以运行多个线程。
(7)线程之间不能互相保护。
(8)多个线程在一个地址空间,共享数据很容易,相互通信的开销比进程小得多。
(9)实现并行操作的效率高。
3.4.2 线程调度和同步方式
线程调度和进程调度很类似,调度方式取决于不同的调度算法,调度算法决定一个线程所能运行的时间以及下一个运行的线程。DCE中的线程具有优先级,优先级对于调度算法来说是有意义的。高优先级线程比低优先级线程更重要,因而会更好地提供条件先处理,即不但优先调用,而且可以获得更多的CPU时间。
DCE有三种调度算法,如图3-3所示。

图3-3 DCE的调度算法
(1)第一种调度算法:FIFO(先进先出法)。首先在优先队列中从高到低进行搜索,以找到一个或若干个具有最高优先级的线程,只要调入就可以一直运行,直到阻塞或退出为止。原则上,被选的线程可以按需求运行完。当结束之后,从运行队列中将它删除,再从中选一个优先级最高的再处理。
(2)第二种调度算法:RR(回转法)。对于优先级依次运行一段固定的时间片。一个线程如果在时间片使用完前被阻塞或退出,才会(暂时)从队列系统中删除。如果它用完了所有时间片,它将被挂起并放到队列末尾。例如在图3-3(a)和图3-3(b)中,线程A、B、C在需要的情况下可以一直交替运行下去,当高优先级线程一直需要运行的情况下,中、低优先级线程D、E将永远得不到运行机会,即可能“饿死”。
(3)第三种调度算法:默认算法。它以回转算法运行所有队列上的线程,但优先级高的进程可以得到更多的时间片。按这种方式,所有线程都有运行机会,而不会出现RR算法中的“饿死”现象。
DCE中提供了两种线程同步方法:互斥与条件变量。
当必须避免多个进程或线程同时访问同一资源时,应该使用互斥。当一个线程在操作链接表时,所有其他线程都不能再访问这个链接表。通过要求一个线程首先确定与链接表相关联的互斥体,然后再访问链接表(访问后对互斥体进行解锁)的方法就可以保证操作的正确性。
有三种互斥类型:
(1)快速互斥——对它第二次锁定导致一个死锁。
(2)递归互斥——允许它进行二次锁定。
(3)非递归互斥——对它第二次锁定导致一个错误。
条件变量(Condition Variable)提供第二种同步机制。它们可以与互斥体一起使用。当一个线程需要某个资源时,它使用一个互斥体来获得访问一个保存这个资源状态的数据结构的排它权限。如果这个资源没有处于可用状态,那么这个线程等待在一个条件变量上,这将导致线程自动被挂起和互斥体被释放。以后,当其他条件变量发送信号时,这个等待的线程就会重新启动。
3.4.3 DCE的多线程机制、结构和实现
一个进程中的多线程机制是高速计算机支持并发和并行计算的有效手段。从CMU开发的Mach到OSF1,很多现代操作系统都支持多线程。在多线程的支持下,服务器可以同时处理多个客户机请求,而客户机应用程序的不同部分也能同时并行地执行。这样,系统的性能得到有效的改善。
虽然这种并行性也可用进程来实现,但线程是更细粒度的并行。由于多线程共享一个地址空间,线程之间的通信开销比IPC要小得多,所以自Mach2.0以后DCE都采用基于Posix3.4A线程接口通信规范(PTHREAD)。如果有的OS不支持单进程多线程机制,可利用DCE中的线程库完成与线程调度相关的所有任务。
引入线程既保证程序的顺序执行,从而仍能使用阻塞式系统调用,又能实现更深层次的更细粒度的并行,如一个文件服务器进程可由多线程来实现。
一个线程一般包括PC、堆栈、子线程和状态等。一个进程包括地址空间、全局变量、打开文件、子进程、定时器、信号、信号灯(PV操作)、计数信息等。
一个进程内可有不同线程。属于同一进程的多个线程有相同的地址空间,它们共享全局变量,可以读/写其他线程的堆栈。
线程之间不能互相保护,也不必要。进程之间总是需要相互保护。如果一个线程运行的环境坏了,则所有线程都不能工作;而一个进程坏了,其他进程不受影响。
图3-4是一个用线程实现文件服务器进程的例子。

图3-4 用线程实现文件服务器进程的例子
值得说明的是,由一个派遣线程和多个工作线程来实现一个文件服务器,派遣线程从邮箱(Mailbox)中接收工作请求,检查请求后,它选一个空闲工作线程并传递工作请求。传递方法很多,如可将消息指针写入一个与各线程联系的名字中,然后启动工作线程,工作线程再检查是否有共享块满足请求。若满足,则直接由缓冲区读取;否则发送请求至磁盘,自己休眠,等磁盘读完成。派遣线程又去接新的工作请求。多线程比单线程进程更具有并行性,它可以实现真正的并行。
3.4.4 线程包及其原语
线程包(Threads Package)是一组与线程有关的Primitive,也称为原语。线程的操作通过一组原语。DCE线程包共有54条原语,可提供DCE的用户调用,其中主要的有下述6类33条。
1.线程管理
Create:创建新线程。
Exit:退出线程。
Join:类似UNIX的Wait。
Detach:父线程不等待子线程结束。
Cancel:杀死本线程。
Setcancel:允许其他线程杀死本线程。
2.模板(Template)
Attr-create:设参数创建模板。
以下在模板内进行。
Attr-delete:删除线程模块。
Attr-setprio:设优先权。
Attr-getprio:读优先权。
Attr-mutexattr-cr:生成M模板。
Attr-mutexattr-delete:删除M模板。
Attr-mutexattr-setkind-up:设M类。
Attr-mutexattr-getkind-up:读M类。
Attr-condattr-cr:生成条件参数模板。
Attr-condattr-delete:删除条件参数模板。
3.Mutex互斥
Mutex-ini:初始化Mutex。
Mutex-destory:删除Mutex。
Mutex-lock:Mutex加锁。
Mutex-trylock:加锁失败时,试加锁。
Mutex-unlock:解锁一个互斥体。
4.条件变量(Condition Variable)
cond-init:创建一条件变量。
cond-destroy:删除条件变量。
cond-wait:等待某一条件变量,直到接收到某一信号或广播。
cond-signal:当在等待某一变量时,唤醒至多一个线程。
cond-broadcast:唤醒所有的线程。
5.线程全局变量管理
keycreate:创建一个全局变量。
setspecific:给全局线程变量指定一个指针值。
getspecific:从全局线程变量读一个指针值。
6.线程调度管理
setscheduler:设置调度算法。
getscheduler:读当前调度算法。
setprio:设调度优先权。
getprio:读取当前调度优先权。
3.4.5 应用举例
图3-5是用DCE线程实现远程过程调用(RPC)的框图。客户机线程要调用服务器上线程的步骤是:

图3-5 用DCE线程实现远程过程调用的框图
(1)客户机线程向本地的RPC接受线程提出RPC调用。
(2)接受线程接收客户机线程的RPC请求。
(3)客户机线程通知计时器为RPC计时,接受线程通过网络把RPC请求发往服务器。
(4)服务器收听线程接收到来自客户机线程的RPC请求,调用相应的服务线程完成客户机线程的服务请求。
(5)服务器应用线程启动计时线程为RPC计时。
(6)服务线程完成计算后,将结果返回给客户机线程。
由于服务器所有线程都在服务器进程中,因此它们之间创立、转换比在子进程中要快得多。这是因为线程之间的转换并不包括对操作系统的调用开销及它的上下文转换开销。