个人博客:http://blog.kunpw.cn
第三章.分布式程序设计语言
1.分布式程序设计语言概述
1.1 分布式应用程序的分类
在分布式计算系统中,对应用程序进行程序设计的层面有四:减少单个计算的周转时间、增加可靠性和可用性、使系统某些部分提供特殊功能、固有的分布式应用。
分布式应用程序分类:
-
并行、高性能应用程序:
- 粒度是在通信之间的计算时间的长短,并行应用程序可按粒度划分:
- 大粒度并行应用程序用大部分时间进行计算,而不经常通信;
- 细粒度并行应用程序经常通信,适合紧密耦合分布计算系统;
- 松散耦合分布计算系统通信开销非常大,只适合大粒度并行计算,但大粒度并行计算也可适用紧密耦合分布计算系统;
- 粒度是在通信之间的计算时间的长短,并行应用程序可按粒度划分:
-
容错应用程序:
- 分布计算系统具有允许部分失效的特性,一个处理机的故障不影响其它处理机的正常工作,即可靠性高;
- 除此之外,程序和数据还可在若干处理机上复制,由此进一步增加可靠性;
- 抵御自然灾害,需要使用处理机在地域上松散耦合;
-
具有专用功能的应用程序:
- 每个服务可以使用一个或多个专用处理机,用户通过网络请求这些服务;
- 易于增加新功能或改进现有功能,通过增加新的处理机即可实现;
-
固有的分布式应用程序:
- 有些应用程序本身就是分布的,这种应用程序必须在分布式硬件上运行,如电子邮件;
1.2 分布式程序设计和顺序程序设计的区别
在分布计算系统上实现这些应用程序的活动叫做分布式程序设计。
分布式程序设计和顺序程序设计的三个区别:
- 使用多个处理机:
- 系统具有把一个程序的不同部分分配到不同处理机上执行的能力;
- 处理机合作:
- 各个进程必须能相互通信和同步;
- 处理部分失效:
- 能对系统的部分失效进行检测并恢复;
1.3 分布式程序设计语言的分类
- 按并行模式来分:
- 顺序进程并行语言:
- 基本模型:一组顺序进程,并行运行,并能通过报文传递进行通信;
- 语言:C、C++、FORTRUN、SR、Ada、CSP……
- 具有内在并行性的语言:
- 函数式语言:无副作用,允许以任意次序(包括并行)计算表达式;
- 逻辑语言;
- 面向对象语言;
- 这些模型中并行的粒度比基本的通信顺序进程模型中小很多;
- 顺序进程并行语言:
- 按通信模型来分;
- 容错模型和技术:
- 处理机故障:提供故障检测机制;
- 故障处理模型:
- 理想情况,系统对程序员隐匿全部处理机故障;
- 系统给程序员提供高层机制,使程序员能够描述哪些进程和数据是重要的,以及发生崩溃后怎样恢复;
- 实现可靠性:
- 程序设计容错:
- 向前恢复:试图确定错误所在,并在此基础上改正包含错误的系统状态,高级语言中的异常处理机制提供了此系统结构;
- 向后恢复:通过把系统恢复到错误发生前的状态来改正系统状态,恢复阻塞方案提供了这种系统结构;
- 通信容错:
- 通信容错依赖于使用的通信方式及故障类型;
- 程序设计容错:
2.并行性的支持
2.1 并行性的概念
因为分布计算系统有多个处理机,所以可以把程序分成若干部分放到多个处理机上同时运行,这就是并行性。
- 伪并行性:把程序表示为一组并行运行的进程,但并不一定在不同的处理机上同时运行。例如一瞬间只运行一部分可以独立运行的进程。
2.2 并行性的表示
表示并行性的一个重要因素是语言的并行单位,在顺序语言中,并行单位是整个程序,但在分布式程序设计语言中,并行单位可以是进程、对象、语句、表达式或逻辑语句中的子句。
- 进程并行:一个进程是一个逻辑处理机,顺序地执行代码,并具有自己的状态和数据的过程
- 隐式创建进程:先说明进程类型,以说明该类型的变量的方式创建这类进程。进程总数在编译时确定,把进程变换到处理机比较容易,但必须提前知道进程数,限制了应用程序的类别;
- 显式创建进程:可以传参用于设置进程间的通信通道,进程创建不使用参数时,必须显式传参,即需要一个机制建立发送该参数的通信通道;
- 需要某些设施防止进程试图与已结束的进程通信;
- 对象并行:对象是包含有数据和行为的完整单元,与其它单元仅能通过某种报文传递的方式相互作用;
- 语句并行:语句被分成组,且并行执行;
- 函数并行:
- 子句并行:
2.3 并行计算到物理处理机的变换
如何把并行计算分布于可利用的处理机上?把计算对处理机的分配叫做变换。
可编程的变换,即用户控制的变换由两步组成:
- 把并行单元变换到物理的处理机上;
- 使用局部变换对同一处理机上的单元进行调度,通常使用分配给并行单元的优先权;
- 把并行单元分配给处理机有三种分配方式:
- 编译时确定处理机;
- 运行时确定处理机;
- 完全不确定;
3.进程通信和同步的支持
在各处理机上并行运行的一个程序的不同部分如何合作,包括通信和同步两种相互作用。
进程通信的表示有两类:共享数据和报文传递。
同步相关的问题是非确定性,一个进程可能要等待来自其它一组进程中任何一个进程而不是指定进程的信息,它预先并不知晓要接收来自哪个组或者哪个进程的报文信息。
3.1 报文传递
基本原语是从一个进程(发送者)到另一个进程(接收者)的点到点报文。
发送者:通过发送报文或调用远程过程显式地发动相互作用;
接收者:
- 显式接收,指明接收哪些报文以及报文到达时采取的动作;
- 显式报文接收使接收者能对报文的接收进行更多的控制,以及可能实现精确控制;
- 某些语言提供接收次序的控制,报文可按照报文的类型、发送者或内容改变这个顺序,如文件服务员可能需要先处理读请求;
- 如果语言不支持有条件或有序接收,那么需要此服务的应用程序就需要记录已被接收但还未处理的请求;
- 隐式接收,在接收者内自动调用程序,通常在接收进程中创建一个新的线程;
- 隐式接收在命名方面总是非对称的;
另一个问题是相互作用的进程双方的寻址(命名),即发送者将报文发给谁,接收者又从何处接收报文?双方的命名分为直接命名和间接命名:
- 直接命名:指示一个指定的进程,如果发送者和接收者相互命名,则该命名对称;
- 对称的直接命名方案不能表达非确定性行为,因而使用这些方案的语言需要具有单独的非确定性机构;
- 间接命名:包括一个中间对象邮箱,即
- 邮箱就是全局名字,发送者发送报文给邮箱,接收者也从邮箱接收报文;
- 将邮箱作为值处理,该值作为报文的一部分传送,这种可选设施允许表达高度灵活的通信模式;
报文传递通信模式有以下几种:
- 同步和异步点到点报文:点到点报文传送系统的设计问题是同步方式还是异步方式,本质是单向通信,有些麻烦
- 同步报文传送:发送者在接收者接收报文前一直阻塞;
- 优点:接收者仅需为每个发送者缓冲最多一个报文,附加的流控制不改变原语语义;
- 缺点:不灵活,发送者总是要等待接收者接收报文后才能继续工作;
- 异步报文传送:发送者发出报文后即可恢复工作,不阻塞等待;
- 优点:报文传送按序时,使用流控制可以保持发送者和接收者的同步但可能发生死锁;
- 缺点:缓冲器可能溢出,两个解决方案,丢弃或者流控制,后者将阻塞发送者直到接收者接收某些报文为止;
- 同步报文传送:发送者在接收者接收报文前一直阻塞;
- 会合:双向通信
- Ada语言中,会合模型基于三个概念:
- 项说明:属于服务器程序,在语法上像过程说明,一个项有一个名字和零或多个形式参数;
- 项调用:属于客户端程序,类似过程调用语句,给该项和包含该项的进程命名,并供给实际参数;
- 接受语句:属于服务器程序,可以包含一组语句,在调用该项时执行;
- 当进程S调用进程R的一项,R为此项执行accept语句时,在S和R之间发生的相互作用叫做会合:
- 会合时进程同步,并且R执行接受语句,并在执行语句时访问该项中由S提供的输入参数;
- R可以给输出参数赋值,然后再将输出参数传送回S;
- R执行接受语句后,S和R继续并行工作,S不再阻塞,R仍可为S的请求工作;
- Ada语言中,会合模型基于三个概念:
- 远程过程调用(RPC):双向通信
- 当进程S调用进程R的过程P时,由S提供的P的输入参数被送给R;
- 当R收到调用请求时,执行过程P,并把输出参数送回给S;
- 执行P期间,S阻塞,直到输出参数返回;
- RPC通常但不一定是隐性的,并会在接收者中创建一个新的控制线程;
- 透明和非透明RPC;
- 基本的RPC有缺陷如下:
- 通信开销:客户使用相同数据调用几个过程时,RPC不支持远程对象,客户每次调用都要传送一次该数据;
- 缺乏并行性:顺序执行,调用者被挂起直到获得结果;
- 缺乏灵活性:RPC客户只能使用有限的几种服务,新的过程必须由程序员准备;
- 一到多报文传送:即广播或组通信,对操作系统内核及语言运行时系统有效
- 相较于点对点通信优点:
- 向多个进程进程发送数据时,一个小组广播比多个点对点报文快;
- 能保证报文的次序;
- 广播以同一次序传送给所有目的地,对于复制数据的一致性更新很有效;
- 基于CSP建立的广播顺序进程(BSP)系统中,原语必须具有可靠性,且广播异步,发送者不必等待所有其余进程准备好接收报文,BSP定义了两种广播:
- 非缓冲广播报文仅被已准备好接收报文的那些进程接收;
- 缓冲广播报文被接收进程所在的机器缓冲,所以每个进程最终都会接收该报文;
- 相较于点对点通信优点:
3.2 共享数据
两个进程访问同一个变量,可以实现另一种通信方式,一个进程对此变量进行设置,另一个对它进行读操作。
同机器上两个进程:变量在本机器上存储,可以直接实现通信;很多分布式程序设计语言使用共享变量进行同一处理机上伪并行通信。
不同机器上两个进程:可以向存有该变量的机器发送报文,访问该变量,与报文传送比较:
- 优点:
- 报文一般在指定两个进程之间传递信息,共享数据可以被所有进程访问;
- 对共享数据赋值能立即起作用,而报文从发送到接收有一个可觉察的延迟;
- 缺点:
- 共享数据要求防止对个进程同时修改同一数据;
两种分布进程的共享数据方法:适用于实现原子事物处理的语言
- 分布式程序可以使用对象实现数据共享,两个进程可以通过调用对给定对象的操作间接地相互通信,因为对象控制对其管理的数据访问,所以还可以实现其它访问该数据的进程的同步;
- 分布式数据结构:
- 可由若干进程同时处理,首先在Linda语言中引入,使用元组空间(Tuple Space)TS概念实现,TS在概念上是一个共享存储器,是为程序的所有进程共享的全局存储器,其基本单位是元组,数据结构有序;
- 如
["jones",31,true]
是一个有三个段的元组,元组结构为一个字符串、一个整数和一个布尔值; - 对TS定义了三个原子操作:元组没有地址,对它的寻址是根据其内容进行的
- out:向TS中加入一个元组,
out("jones",31,true)
; - read:读取TS中的一个元组,
read("jones",var age, var married)
; - in:读取TS中的一个元组并删除,
in("jones",var age, var married)
; read
和in
匹配符合的元组,匹配多个则可任选一个,匹配不到则阻塞该操作和调用该操作的进程,直到另一个进程使用out
添加一个匹配的元组为止;- 没有修改操作,要修改必须删除再添加,且原子操作即使同时被两个进程删除依然会呈现次序,只有一个会成功,另一个被阻塞;
- out:向TS中加入一个元组,
- TS的分布实现中,运行时系统处理元组在处理机上的分布具有以下几种策略:
- 在所有处理机上复制整个TS;
- 以散列方式把元组放到指定处理机上;
- 把元组存储到执行过
out
操作的处理机上;
- 共享的逻辑变量:逻辑变量具有单赋值性质,最初未赋值,一旦赋值则不能修改
goal_1(X,Y),goal_2(X,Y),goal_3(X)
- 变量X是这三个进程的通信通道,最初未赋值,一旦被某个进程赋值,则另外两个进程可使用此值;
- 使用P1、P2、P3并行运行,则Y是P1、P2的通信通道,如果Y用于从P1向P2发送一个报文,那么P2会挂起直到P1给Y赋值;
- 共享变量实现挂起操作有几个方法,一般是为变量限制进程赋值,即设置权限;
- 由于赋值不能取消,共享逻辑变量似乎只能传送单个报文,事实上把逻辑变量和其它未赋值的变量结合用作下一步通信通道;
- 使用共享逻辑变量实现的技术包括界缓冲器流、一到多的流、不完全报文;
- 缺点:
- 因为仅有一个进程可附加一个通过逻辑变量实现的流,而客户-服务器模型是多到一通信,为了在并行逻辑语言中实现这一点,每个客户必须由自己的输出流,如此有二法构造服务器:
- 服务器为每个客户使用单独的输入流,接收通过每个流发送的报文,这要求服务器知道所有客户的标识,限制了客户的数量;
- 合并所有客户的输出流,把它作为单一的输入流提供给服务器,这样的逻辑操作可用并行语言来表示;
- 因为仅有一个进程可附加一个通过逻辑变量实现的流,而客户-服务器模型是多到一通信,为了在并行逻辑语言中实现这一点,每个客户必须由自己的输出流,如此有二法构造服务器:
3.3 非确定性的表示和控制
进程之间的相互作用模式并非是确定的,有时还取决于运行时条件,因此需要研究如何表示和控制非确定性模型。
通过一个信口间接接收的报文可能是由任意 一个进程发送的,这种原语提供了表示非确定性的方法,但是并未提供控制的方法,大多数的程序设计语言使用分开的非确定性的结构,有以下两种结构类型(下面这段我也有点懵……):
- 选择语句:用于很多算法语言
- 由保护命令如下组成:保护->语句;
- 保护Guard由一个布尔表达式和某一类通信请求组成,布尔表达式必须无副作用(可能被多次使用)
- 在CSP中,一个保护可以包含从指定进程P中显式接收一个报文,如此一个请求可以成功(P已发送一个报文),也可以失败(P仍然生存并且未发送这个报文);
- 保护本身可以成功(表达式为真请求也成功)
- 失败(表达式为假或请求失败)
- 挂起(表达式为真且请求挂起),此时选择语句整体受阻直到它的所有保护失败(整个选择语句失败没有影响)或某些保护成功(非确定性地选择一个成功的保护并执行相应的语句)
- 保护Guard由一个布尔表达式和某一类通信请求组成,布尔表达式必须无副作用(可能被多次使用)
- 由保护命令如下组成:保护->语句;
- 保护的(Guarded)Horn子句:用于大多数并行逻辑程序设计语言,逻辑程序本质上就不是确定的
- 并行逻辑语言不是对一个给定的谓词一个又一个试验子句,失败时回溯;
- 而是并行地搜索所有子句,并在并行执行期间直到有一个并行执行提交前不允许任何赋值对外部是可见的,即OR不可见性;
- 且不能无限进行,因为并行工作的搜索路径随证明的长度而指数增长;
4.分布式控制描述语言DCDL
DCDL是一种面向语句和顺序的命令型语句,是一种框架控制驱动语言,用于描述一些控制结构,如并行的表示、进程间通信和同步、容错设计。
DCDL通用符号:
4.1 DCDL中并行性的表示
DCDL的并行单元是语句,并行语句可以用有向无环图(DAG)优先图来表示,节点代表语句,有向边代表优先关系,在优先图中不存在冗余的连接。
冗余:如图,因为S1到S2,S2到S4,所以S1到S4的连接是冗余的。
DCDL语句表示:
- 并行语句:
S1||S2||···||Sn
; - 顺序语句:
S1;S2;···;Sn
; - 因此上述优先图可以表示为
S1;[S2;[S3||S4];S5;S6]||S7
; - 语句
S
j
S_j
Sj(1<=j<=n)可以是一个命令列表,保护列表:
G->C
中G是一个布尔表达式列表组成的保护,C是一个命令列表,当保护执行成功时,该命令才可被执行;
4.2 选择语句
选择语句选择其组成的被保护的命令之一执行,如果多于一个命令可被选择,选择将是不确定的。
选择语句表示:[G1→C1ꪼG2→C2ꪼ···ꪼGn→Cn]
[x>=y→m:=xꪼy>=x→m:=y]
:如果x>=y,将x赋予m,如果y>=x,将y赋予m;如果x>=y并且y>=x,则将x或y之一赋予m;
4.3 重复语句
一个重复语句指定其组成选择语句的交互次数,这些语句带保护或不带保护。
重复语句形式:
*[带保护的选择语句]
:- 所有保护都经过时,重复语句终止;
*[不带保护的选择语句]
:- 执行不终止;
(n)[选择语句]
:- 重复n次才终止;
4.4 语句并发(并行)的条件
两个语句并发执行时,可能产生与顺序执行不同的结果。
定义符号:
- R(Si),Si的读集,即在Si中被引用的所有变量的集合;
- W(Si),Si的写集,即在Si中被修改的所有变量的集合;
对于两个并发执行的语句S1和S2,必须满足三个条件才能使它们并发执行的结果与它们以任意次序顺序执行的结果相同:
- R(S1)∩W(S2)=∅;
- R(S2)∩W(S1)=∅;
- W(S1)∩W(S2)=∅;
使用S1||S2
表示语句S1和S2满足这三个条件,可以并行或并发执行。
4.5 DCDL中的通信
DCDL采用异步点对点报文传递,报文通过异步静态通道传递给指定的接收方进程。
命令:
- 发送命令:
send message_list to destination
,destination是一个进程名或一组进程的关键字all - 接收命令:
receive message_list from source
,source是一个进程名,支持显式和隐式的报文接收,隐式接收为receive message_list
;
异步静态通道是两个进程间的一个FIFO队列,且异步通信可以模拟为同步通信,同步通信如下:
- 发送方:
send message_list to destination
reseive empty_signal from destination
- 接收方:
receive message_list from sender
send empty_signal to sender
4.6 DCDL中的通信容错
最简单的通信容错是使用故障-停止模型的报文传递,一个处理机要么正常工作,要么完全停止。
ve message_list`;
异步静态通道是两个进程间的一个FIFO队列,且异步通信可以模拟为同步通信,同步通信如下:
- 发送方:
send message_list to destination
reseive empty_signal from destination
- 接收方:
receive message_list from sender
send empty_signal to sender
4.6 DCDL中的通信容错
最简单的通信容错是使用故障-停止模型的报文传递,一个处理机要么正常工作,要么完全停止。