是一种通用的面向并发的编程语言,它由瑞典电信设备制造商爱立信所辖的CS-Lab开发,目的是创造一种可以应对大规模并发活动的编程语言和运行环境。Joe Armstrong 开发的。

初步认识:

erlang 世界的基石,了解下面的,就可以对 erlang 有一个简单的认识:

  • everything is a process.
  • process are strongly isolated.
  • process creation and destruction is a lightweight operation.
  • message passing is the only way for processes to interact.
  • processes have unique names.
  • if you know the name of a process you can send it a message.
  • processes share no resources.
  • error handling is non-local.
  • processes do what they are supposed to do or fail.

process(进程)

和 OS 的 process 概念几乎一样,就是 PCB(Process Control Block), 以及围绕 PCB 的 heap,stack,mailbox 等内存空间。只不过,Erlang 的 process 的 memory footprint 很小很小,一个刚初始化的 process,也就占用几百个 words。Erlang 的 process 源自 actor model 的思想,是常见的 concurrency model 的一种。

routing(路由)

当系统中有了无数独立的 process 之后,一个迫切的问题是如何找到我们需要与之胡操作的 process。这就是路由的过程。 1. 通过唯一的 pid 来定位到目标 process; \2. 通过和 pid 一一对应的名字来定位到 process; 3. 通过路由来寻找,这个叫做 name registry。

error handling(错误处理)

靠 process 之间的 link 完成的。link 是一个作用域双方互相监视的机制。link 是双向的连坐机制。对于有些影响不大的问题,并不需要如此强有力的手段,于是有了 monitor。

Erlang/OTP 的 supervision tree 就是基于link/monitor 这样简单的机制构建起来的,而之所以 erlang 可以任性的 let it crash。其中很大一个原因就是 process 非常轻量,构建和销毁的代价无非就是创建和回收若干个 C structs。

concurrency(并发)

Erlang 的并发模型使用了 actor model。就是说每个 actor 和其他 actor 通讯的方式能且仅能通过 message passing 而完成。因而,每个 actor 有其独立的 mailbox,用于接收消息。

为了调度数量庞大的 processes,erlang VM 有自己的 scheduler — 在 SMP (简单理解为多核)环境下,每个 CPU core 有一个 scheduler。和 OS 的 scheduler 类似,有 run queue 和 wait queue,scheduler 从 run queue 上取出下一个 process,执行其代码。如果 process 没有通过诸如 receive message 这样的动作显式地将自己阻塞并被调度出去,scheduler 会再一个固定的 cycle 后把 process 调度出去。

所以从 erlang 程序员的角度来看,erlang 是 preemptive scheduling,并且,这种每个 process 获得的 CPU 时间几乎固定,时间在可预知的时间内得到调度。成为 soft realtime。

Hard realtime:CPU 的时钟终端。会再固定的间隔被触发,丝毫不差,风雨无阻。

进一步了解:

Message passing(消息传递)

处理 binary 的方式,大的 binary(通过 binary allocator分配出来的)放在 share pool 里,发消息只发一个 reference,对方收到以后,通过 reference 可以直接找到数据,避免无谓的拷贝。这个binary 要记录 refcnt,对「收看」和「看过的」行为要记录,保证 binary 不会在 consumer 还未使用前就被释放,也要保证不会再全部使用者使用完了之后还未释放。

scheduling(调度)

Cooperative multitasking:调度的控制权交给各个 process,相信「人性本善」。使用一些 CPU 后就会主动做 context switch。调度的时间是可预知的(因为 process 自己的代码去 yield),实现起来简单,但很难保证每个 process 对时机把握的尽善尽美,一旦有不合理实现的 process,通过无休止的大循环占用过多的 CPU,就会把其他 process 饿死。

Preemptive multitasking:前者的改进。调度的控制权把握在 OS 手上,系统唯美个 process 的 CPU 使用时间设定一个上限,通过 CPU 时钟中断触发 scheduling,如果当前 process 达到这个上限后会被调度出去,这种方式调度的时机不可预知,可可能发生在 process 代码的任何位置,所以实现起来,尤其是 context switch 会复杂一些。

  • 从erlang application 角度来看,erlang VM 的调度是 preemptive 的,因为每个 erlang process 不知道耶不用关心自己会在什么时候被调度;
  • 从BEAM scheduler 实现的角度来看,又是 cooperative 的,每个 process 的 main 函数(process_main)里会显式调用erts_schedule,看看自己的 reduction count 是否耗尽,如果是,做一个 context switch。

reduction 是 erlang 用于检查 process throughput 的工具。类似 linux 的 nice value。一次 reduction 可以简单认为是一次函数的调用。

erlang VM 的 scheduler是标准的 scheduler:每个 core 上一个逻辑意义的 scheduler,有多级队列,按照 process 的优先级区割。max 是 erlang 系统内部保留的优先级,往下是 high / normal / low,按这个顺序依次调度。

Timer / timeout 这样的 event 如何处理的?使用 timer wheel

OTP

全称是 Open Telecom Platform。

如果把 erlang VM 看作是 OS 的 kernel,那么 OTP 就是这个 kernel 的前天组成部分:stdlib,initd, syslog,OAM,等。

  • gen_server:通用服务器行为。
  • gen_fsmgen_server的实现,处理有限状态机。
  • gen_eventgen_server的实现,处理事件。
  • supervisor:负责启动、停止和监视它的子进程。基本思想是它要保持它的子进程有效,必要的时候可以重启他们。
  • application:类似于组件。

release

一个 release 包含若干个 application,每个 application 的版本,之间的依赖关系,启动顺序,需要加载的模块,都在 release 文件中说明。

  • 如何以 daemon 方式运行;
  • 如果以 daemon 方式运行,如何 attach 进行,便于调试;
  • 如果在不同的机器上把你的软件练成一个 cluster;
  • 如何在 release 之间 upgrade/downgrade;

缺点

  • 学习曲线陡峭。
  • immutable 引入的无处不在的内存拷贝,拖累了整个系统的效率。

资源: Erlang OTP 设计原理文档