《Erlang程序设计》学习笔记
Contents
动态代码载入
每当调用 someModule:someFunction(…) 时,调用的总是最新模块里的最新版函数,哪怕当代码在模块里运行时重新编译了该模块也是如此。
在任一时刻,Erlang允许一个模块的 两个版本 同时运行。
保存预处理的输出
erlc -P some_module.erl
它会生成 some_module.P 文件
表达式和表达式序列
任何可以执行并生成一个值的事物,都被称为表达式。
表达式序列,是由一系列逗号分隔的表达式。它们在 -> 箭头之后随处可见。表达式序列 E1, E2, E3,…,En 的值,被定义为序列最后那个表达式的值,而该表达式在计算时可以使用 E1, E2 等表达式所创建的绑定。定价于 LISP 里的 progn
函数引用
- fun LocalFunc/Arity
- fun Mod:RemoteFunc/Arity
包含文件
-include(FileName).
包含库的头文件:
-include_lib(Name).
列表操作
++
A ++ B
即 A 列表和 B 列表相加
1> [1,2,3] ++ [4,5,6].
[1,2,3,4,5,6]
2>
–
A -- B
从列表 A 中移除列表 B 。移除的意思是 B 中所有元素都会从 A 里去除。请注意,如果符号 X 在 B里出现了 K 次,那么A只会移除前K个X。
2> [1,2,3,4,5,6,1,2] -- [1].
[2,3,4,5,6,1,2]
3> [1,2,3,4,5,6,1,2] -- [1,1].
[2,3,4,5,6,2]
4>
短路布尔表达式
- Expr1 orelse Expr2
- Expr1 andalso Expr2
下面的并不是短路表达式:
A or B
A and B
这种两边的参数总会被执行,即使表达式的真值只需要第一个表达式的值就能确定也是如此。
设置代码搜索路径
查看
code:get_path().
设置
erl -pa Dir1 -pa Dir2 .... -pz DirK1 -pz DirK2
pa 表示添加到搜索的开头 pz 表示添加到搜索的结尾
导出所有函数
-compile(export_all).
系统启动时执行一组命令
在用户主目录的 .erlang 文件里的所有命令,都会有 erlang 启动时执行:
[12:41:01] emacsist:~ $ cat ~/.erlang
io:format("Hi, I'm in your .erlang file ~n").
[12:41:05] emacsist:~ $ erl
Erlang/OTP 19 [erts-8.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Hi, I'm in your .erlang file
Eshell V8.3 (abort with ^G)
1>
查看用户主目录位置:
init:get_argument(home).
在命令提示符里编译和运行
erlc hello.erl
erl -noshell -s hello start -s init stop
使用 Escript 运行
文件 hello 的内容:
#!/usr/bin/env escript
main(Args) ->
io:format("hello world~n").
Args 是一个原子列表
执行:
chmod u+x hello
./hello
停止Erlang
有时Erlang停不了下来,以下是可能的原因
- shell 没反应
- Ctrl-C 被禁用
- erlang 启动时设置了 -detached 标识
- erlang 启动时设置了 -heart Cmd 选项。它会建立一个操作系统进程来监控Eralng的操作系统进程。如果Eralng的操作系统进程崩溃了,Cmd就会被执行。通常Cmd只是简单地重启Erlang系统。
- 某处出现了严重的问题,导致Erlang进程为僵尸进程
分析转储文件 erl_crash.dump
crashdump_viewer.start().
获取帮助
erl -man erl
erl -man lists
基本并发函数
Pid = spawn(Mod, Func, Args)
Pid = spawn(Fun)
Pid ! Message
receive ... end
带超时的接收
receive
...
after Time ->
Expressions
end
Time 为毫秒数
注意, receive 里面的表达式可以忽略。而只保留超时
receive
after T ->
Exp
end.
如果 Time 为0,则会先尝试匹配 receive 里的模式,如果没有匹配,则直接执行 after 的表达式,而不会挂起。
获取最大进程限制数
erlang:system_info(process_limit)
这个数可以通过虚拟机参数 +P 100000 来设置
注册进程
- register(AnAtom, Pid)
- unregister(AnAtom) 移除 AnAtom 的注册信息。某个注册进程崩溃了,就会自动取消注册
- whereis(AnAtom) -> Pid | undefined 查看AnAtom是否已经被注册。
- registered() -> [AnAtom::atom()] 返回所有注册进程列表
并发Erlang的错误处理
它是建立在 远程检测 和 处理错误 的概念之上。即选择让进程崩溃,然后在其他进程里纠正错误。
监控进程可以 实现跨机器 的透明动作,这是编写容错式系统的基础。不能在同一台机器上构建容错式系统。
即如果某个 Erlang 进程出了点小问题,可以尝试用 catch 或 try 语句来修复它。但如果修复失败,就应该直接崩溃,让其他进程来修复这个错误。
进程链接
调用函数 link(Pid) 就会在调用进程和Pid之间创建一个链接。
注意,链接是 双向 的。如果 P1 调用了 link(P3) ,则 P1 和 P3 就是相互链接了。
设立防火墙
即不想系统里的错误继续扩散
在某个进程上,执行 process_flag(trap_exit, true) 这样子,错误扩散到该进程,就不会继续扩散了。
监视
监视是 单向 的。如果A监视B,而B挂了,就会向A发送一个退出消息,反过来而不会如此。
RPC调用
rpc:call(Node, Mod, Func, [Arg1, Arg2, Arg3,...,ArgN]).
Magic Cookie
设置:
erl -setcookie ABSFSDFSADFSDFSDFSDFSF
这一般只是在测试时使用,因为使用 ps aux 可以查看它的 cookie 。
或保存在 ~/.erlang.cookie
或使用函数来设置: erlang:set_cookie(node(), C) 即将cookie设置为C
使用套接字的分布式模型
lib_chan 模块
Erlang中的端口
通过它来控制外部进程以及通信。Erlang负责启动和停止外部程序,还可以监视它,在它崩溃后重启。外部进程,称为 端口进程 ,因为它是通过一个 Erlang 端口控制的。
使用端口与外部进程,和使用套接字是不同的。如果使用端口,它会表现得像一个 Erlang 进程,这样就可以链接它,从某个远程分布式 Erlang 节点向它发送消息等等。如果使用套接字,就不会表现出类似进程的行为。
相连进程
创建端口的进程被称为该端口的相连进程。所有发往端口的消息,都必须标明相连进程的PID,所有来自外部程序的消息都会发往相连进程。
创建端口
open_port(PortName, [Opt]) -> Port
向端口发送消息
Port ! {PidC, {...}}
Erlang中调用外部语言代码
这是一种不安全的做法。
ETS与DETS
它们可以在进程间共享!(但它不会被GC)
它们保存的,都是元组。默认情况下,第一个是该表的 键。
CRUD: - ets:new或dets:open_file - insert(TableId, X) X为元组,或元组列表 - lookup(TableId, Key) - dets:close(TableId) 或 ets:delete(TableId)
表的四种类型:
- set
- ordered_set
- bag
- duplicate_bag
DETS的最大文件大小是 2GB
Mnesia数据库
它是用Erlang编写的数据库。 它支持事务,并有自己的查询语言。支持保存在内存,也可以保存在磁盘。在集群里,不同的节点可以是不同的存储类型。
它保存的是 Erlang 中的 记录 (record)
初始化
mnesia:create_schema([node()]).
在非集群里,它会自动创建一个 Mnesia.nodename@hostname 的目录来保存数据库。也可以在启动时,指定保存的目录,而不是默认的 Mnesia.nodename@hostname ,像这样子:
erl -mnesia dir '"/home/emacsist/Mnesia"'
注意,单引号里面的双引号。
启动
mnesia:start()
其他的就不继续写的,详细直接看文档。
代码性能分析工具
- cprof :统计各个函数被调用的次数。在 online 系统上运行,会增加 5~10%的负载
- fprof :显示调用和被调用函数的时间,结果会输出到一个文件。重量分析,会显著增加系统负载
eprof :测量erlang程序是如何使用时间的。它是 fprof的前身,适用于小规模的性能分析。
cprof:start(). 执行你的代码 Mod:xx cprof:pause(). cprof:analyse(Mod) cprof:stop().
代码覆盖测试
cover:start().
cover:compile(你的模块).
你的模块:函数().
执行一段时间后。。。。。
cover:analyse_to_file(你的模块).
生成交叉引用
注意,只有在代码编译时设置了 debug_info 才可以使用 xref 模块
erlc +debug_info *.erl
erl
1>xref:d('.').
跟踪消息与进程执行
参考手册下面的这个文档
erlang:trace/3
配置错误记录器
标准错误记录器
erl -boot start_clean
它创建一个适合进行开发的环境,只提供一种简单的错误记录形式。(不带参数的 erl 命令,就等同 erl -boot start_clean)
erl -boot start_sasl
它会创建一个适合运行生产系统的环境。