需要进一步整理。
Erlang主要分为两部分,首先简要总结语言形式的特点和代码写作风格,然后总结配置Erlang时的种种问题。
Erlang主要分为两部分,首先简要总结语言形式的特点和代码写作风格,然后总结配置Erlang时的种种问题。
Log #2
0001
我又搞错了,Mathematica中的东西在Erlang里面基本无法实现。毕竟两者基本的设计目的就有很大差别。今天重点看过了 Programming Erlang的第八、九、十章,然后草草地翻阅到了第十六章。八九十章着重介绍如何创建和消灭进程,以及进程之间的通信。从这里开始Erlang的神奇之处就逐渐显示出来了。一个Erlang进程,包含了如下信息:
1)进程标识
进程标识是在进程创造出来的时候分配的,这个标识是由创造进程的spawn表达式返回的值,这个值必须同一个变量相关联,否则就会被当做垃圾回收掉。
2)它会接收怎样的信息
之前所写的Erlang代码,都是我们从控制台发送的消息。当这段程序执行完毕之后,执行(对应英文是"evaluate"而不是"run")的结果返回到控制台。但是如果需要这个结果的不是我们而是另一个进程呢?于是进程之间就需要以某种方式传递信息。虽然说通信是接收消息和发出消息共同构成的,但这里只说“接收”而非“发出”,原因在于一个进程必须接收消息才能执行,否则就会被Erlang虚拟机当做垃圾回收掉。但是接收到信息之后,进程可以只进行运算而不发出消息,当然也可以发出信息,也有可能立刻死掉(如果这个消息就是要终止这个进程的话)。
更具体地说,一个进程其实就是一个应激规则库,库中每一个规则包含一个模式和对应的行为。这个模式用来匹配它所接受到的消息。由于消息本身是用Erlang的数据结构表示的,故此处的模式就是匹配这些数据结构的模式。具体表示如下:
receive
MessagePattern1 [when is_Predicate()] ->Action1;
MessagePattern2 [when BooleanExpression] ->Action2;
...
end
其中MessagePattern i 就是用来匹配消息的模式。when是一个关键字,when后面是求值后能够返回一个真值的表达式。它可以是Erlang中的谓词函数,也可以是布尔表达式。模式匹配的结果要保证when后的表达式值为真。
这其实就是人工智能中所提到的agent,它是由一系列的if-then构成的应激机制。然而agent只是一种概念,因为if-then中的if有可能是通过类似Erlang的消息机制传递进来,也可能需要通过agent本身去学习。
3)生存周期
生存周期是有两种方式定义的。一个是作为receive的一个备选项,一个是将receive置入一个递归函数中。作为receive的备选项是 after,它提供了当进程因为某种原因在可接受的时间内没有接收到任何消息时采取的措施。erlang内部设置了进程接收消息的一个时间,在这个时间内如果此进程没有接收到消息进而产生任何动作,那么这个进程就会被挂起,虚拟机会去响应其它进程,直到这个进程收到消息为止。有了after,进程就会在超时之后产生一个和接收到消息一样的动作,然后被终止掉,等着garbage collector来收尸体。
另外一种情形涉及到进程的常用写法:
loop()->
receive
MessagePattern1 [when is_Predicate()] ->Action1; loop();
MessagePattern2 [when BooleanExpression] ->Action2; loop();
...
end
这就意味着,这个进程的应激机制被放进了一个无限递归的无参数无返回值的函数中。当它执行完某个action之后不会消失,而是一直留在内存里等待它的戈多。需要注意的是,这里的loop是一个尾递归。书中对尾递归做了很简单但是相当精辟的陈述:尾递归就是当前一次迭代产生的表达式经过求值之后而非压栈之后才进入下一次迭代,如果这里是线性递归,由于没有终点因此当前的表达式会一直压栈而不会执行。我以后会写尾递归专题文章,这里就不赘述了。
4)死亡信息
就像人一样,有的人离开这个世界之前会大声呼救,有的人离开这个世界时就像我爸一样静悄悄。Erlang进程在被回收之前也会有类似的呼叫行为,它会产生一个信息向所有其他的进程广播,不过当然呼叫不呼叫全看这个进程怎么定义,其他程序也可以定义听见或者听不见。以下情况需要特别讨论:
a.誓死盟约link()
在一个进程Prog1中可以通过link(Prog2)将两个进程建立这种“誓死盟约”。如果Prog1是正常死亡,比如是完成任务后通过返回一个 normal信息或者执行exit()“寿终正寝”,那么Prog2会收到这个信息但不采取任何行动(当然或许听到这个消息会有点伤心)。但是如果 Prog1产生了一个错误比如除零,或者产生了其它任何一种不是正常终止的消息,那么Prog2也会跟着自杀。
b.独当一面的process_flag(trap_exit, true)
一个进程可以利用process_flag来把自己改造成强健的“异常退出捕获”进程,或者叫做系统进程。系统进程承担起汇报同在誓死盟约中的其他进程的死亡信息,因此首先一般的死亡讯息不会令系统进程自杀,而且当系统进程捕获到一个死亡信息之后,便不会再把此信息广播给其他进程。
c.鬼来电kill
kill是一个标识而不是一个操作,如果一个进程返回的是kill,那么誓死盟约内的所有进程(包括系统进程)都会自杀。
在这里需要补充几点:
1.消息的收发是异步的
这一点决定了消息之于进程并不相当于参数之于函数。消息不是进程的参数,因为如果是这样的话,在进程正在执行某个消息对应的动作时会忽略掉这个时候收到的其他消息。这就好像我坐火车跑到上海去找你聊天,结果你却跑到杭州玩去了。而在真实世界的容错系统中,我到上海找你而你恰好在的情况更不常见。消息并不是直接发送到进程中去,而是留在了进程的信箱中(一个队列)。receive其实是在一封一封地读收件箱里面的信,然后按照信中说的去执行对应的行为。异步确保了系统的容错性,这是并行系统的精髓。
2.进程是可以注册的
这个其实就是把进程标识和一个原子相关联。但这其实没什么用,因为进程标识本身就是变量的值,那个变量就应当说明这是一个什么进程,不需要多此一举了。
好,就语言上来说,基于一个Erlang虚拟机的并发系统就没有问题了。
0010
接下来就是虚拟机本身设置的问题,目前的多进程仅能在一个虚拟机的支持下实现,如果想要在不同计算机上的两台虚拟机上实现,就需要配置虚拟机的 TCP/UDP端口。这些我还没有仔细看,因为关于网络协议我还有很多东西不懂。但是看到这里我突然意识到一个严重的问题,我之前对 Mathematica的了解也仅限于Mathematica语言本身,但是关于MathKernel,也就是Mathematica虚拟机的使用方法却几乎一无所知。大概在十年前,Roman Maeder就已经弄出了不同MathKernel通信的解决方案。事实情况是,仅知道抽象语言,而不知道虚拟机的工作方式,或者至少是虚拟机的使用方法,是没有办法真正实现产品的。然而Mathematica关于这方面很少有公布出来的文档,这很让人郁闷。
0011
热代码替换。这是一项freakin到家的技术,并且几乎只有Erlang能轻松实现。热代码替换指的是服务端脚本运行时将它进行修改,服务端程序会把收到的包含替换代码的消息直接纳入到自己的身体里!这就是说,服务器在进行维护的时候不需要停机,像做解剖手术一样重新把服务器端代码编译一遍,而是在设计的时候就为它创造一个消化系统,使得它可以一边工作一边汲取营养。为什么IBM等大公司没有应用这个技术?这不是因为前瞻性,而是因为传统。因为爱立信是做交换机起家的,尽管不是计算机公司,但是它却比所有的计算机公司都先接触并熟稔于并行架构。抱着Java不撒手的IBM现在傻眼了。此外,erlang 也有动态执行功能,它可以生成一个文本,然后再将这个文本按照erlang代码执行,和Python的exec()是一样的。昨天木鱼提到的进化代码,现在已经具有实现的基础了。如果服务器能够在互联网上搜索代码和信息并补充道其本身的代码中,挖哈哈哈,那可就太恐怖了。
0100
好,那么明天开始补充一下网络知识。然后去找关于MathKernel的内容。如果了解MathKernel间的通信方式,我知道怎么设计基于MathKernel的热代码替换。另外继续看其他库的源码。
0101
欧了。
-EOF-
Log #3
今天事情还没做完按说是不应该上来写日志的,不过我注意到Wolfram耍了一个很奸诈的诡计,就是混淆了“并行计算”和“分布式计算”的区别。
并行计算和分布式计算的共同点是它们都需要很多计算节点来分别完成一个大问题中的各个部分。这些计算节点可能在局域网里不同的机子上,也可以在同一个机子里不同的CPU核上,也可以在同一个CPU核里不同的进程中,总之计算节点是一个抽象的概念。
那么并行计算和分布式计算有什么区别呢?并行计算指的是所有计算节点彼此相互连接,存在传递消息的通路,而分布式计算中的计算节点是存在阶级矛盾的,其中主节点用来将任务拆分成子任务,然后发送给从节点。从节点解决完这个问题之后再去从主节点那里领取一个新的任务,但是从节点之间却并无通信。
M@7 中的ParallelEvaluate更确切地应该叫做DistributedEvaluate,它把Kernel间的runtime通信封装在了 ParallelEvaluate这个函数中,对于用户来说消息传递的过程是不可见的。在M@2.2中事实上还保留了不同Kernel间应用TCP协议进行通信(那个时候还没有多核CPU),在新闻组中翻到了一篇写于1996年的文章,提到了KernelLink的用法,但是在Wolfram官方网站上,这部分文档已经被删除了。
现在的问题在于,如果想应用M@Kernel来实现类似Erlang的并行编程(注意M@中的表达式都是顺序执行的,同一个Kernel所执行的任务之间是没有通信的,因此它不能叫做“并发编程”(concurrency)而只能叫做“并行”),而Kernel又是独立的操作系统进程,Kernel之间的通信就只能通过MathLink来实现。因为M@Kernel无法向控制台返回一个二进制流然后再被调用它的程序捕获,并且即使可以这样也会很慢。
接下来要去看MathLink的文档,当然Programming Erlang昨天没有仔细看的地方今天要看完。
欧了。
P.S.
经济基础决定上层建筑,而经济基础是由具有消费能力的人的需求决定的,而具有消费能力的人的需求是由技术创造,也是由技术满足的。因此,上层建筑高度依赖科技。尽管续大师从之前非常嚣张地同我天朝对抗,到现在开始一边谄媚校内管理员一边进行无力的暗讽,但事实上这些话都是球用也不顶。想要改变什么,你必须要去做些什么,甚至可以说做任何事情都可以,但是必须去做。金胖子向全世界送出导弹,要比向全世界送出主体思想更有说服力。我们现在的整个局面,其实都是由技术基础决定的。大到花40M弄出来的屏蔽软件,小到四六级考试时黄豆大的无线耳机,什么法律,什么规则,全都轻而易举地颠覆了。以后随着技术的发展,无论是政治还是人伦,一切都会不断变化。
我们无从知晓历史,因为记忆是不可靠的。我们也无从了解未来,因为我们无法了解现在。这个世界在走向混沌,我们首先需要关注的自己的生存。你我不必就整个时代发表评论,因为这个时代本身就是由你我的饮食起居影响着。
-module (server).
-export ([start/2, rpc/2]).
%%Name of the module is "server", "start" and "rpc" are the functions can be call externally.
start(Name, Mod)->
register(Name, spawn(fun ()->loop(Name, Mod, Mod:init()) end )).
%%Here the Mod is the name of the module which communicates with the new created process. Name is the name of the new created process.
rpc(Name, Request)->
Name ! (self(),Request),
receive
{Name, Response}-> Response
end .
%%RPC is the remote processing control. the framework is defined in the server module, but the messages which to be sent via RPC are defined in the module below. RPC sends the Request to the process with name Name. self() indicates the process sending message via RPC. the Response sent back from the process with name Name will be passed to the process which uses RPC.
loop(Name, Mod, State)->
receive
{From, Request}->
{Response, NewState}=Mod:handle(Request, State),
From ! {Name, Response},
loop(Name, Mod, NewState)
end .
%%loop is the main body of the new created process. When it received the message via RPC, it will start an action defined in the module below. After preformed the expected action, it will send the Response back to the process, which indicated by From.
This is the core of a server, the module neither defines any acceptable information from client, nor specifies how it behaves. It only create a process with requested name, and initiate it as requested. It also defined a communication framework, so that the client can send message via the framework to access the new created process. Finally, it defines the content of the new created process, which indicates that once the message received, it will act as requested, send a response to client process, and then stand by for next message.In a word, the server is only an executive, which has no prior information about what to hear and what to execute. Just like a main() in C.
-module (name_server).
-export ([init/0, add/2, whereis/1, handle/2]).
-import (server,[rpc/2]).
%%it inherits the definition of RPC from the server module, and has 4 function can be called externally.
%%Client routines
add(Name, Place)->rpc(name_server, {add, Name, Place}).
whereis(Name)->rpc(name_server, {whereis, Name}).
%%Defines the message corresponding to the action to be sent.
%%call back routines
init() -> dict:new().
handle({add, Name, Place}, Dict)->{ok, dict:store(Name, Place, Dict)};
handle({whereis, Name}, Dict)->{dict:find(Name, Dict), Dict}.
%%Defines how the server process behaves.
Client provides not only instructions, but also the introductions to those instructions.
没有评论:
发表评论