第六章 编译并运行程序
6.1 开启和停止Erlang shell
使用erl命令开启Erlang shell, 在shell中调用erlang:halt()来停止系统运行, 调用init:stop()的别名q()来退出shell。
6.2 配置开发环境
在大型项目中, 代码分散在各个目录下, 需要进行相关设置使得Erlang加载器可以找到正确的调用模块和函数。
6.2.1 为文件加载器设定搜索路径
- 使用*code:get_path()获取当前系统的加载路径。
- 如果需要添加一个新目录到加载路径的开头, 可以使用code:add_patha(Dir)。
- 如果需要添加一个新目录到加载路径的末尾, 可以使用code:add_pathz(Dir)。
- 如果需要查看所有的已加载的模块, 可以使用code:all_loaded()。
- 如果需要查看发生冲突的模块名, 可以使用code:clash()。
- 以上函数皆可放置在用户目录下的.erlang文件中。
- 或者以以下形式启动shell:
erl -pa Dir1 -pz Dir2
-pa Dir1 表示把Dir1添加到代码搜索路径开头
-pz Dir2 表示把Dir2添加到代码搜索路径末尾
6.2.2 在系统启动时批量执行命令
因为.erlang文件的优先级比较高, 且可以在此文件中添加任意的Erlang代码, 因此可以通过此文件来定制不同行为的程序。
6.3 运行程序的几种不同方法
测试程序
-module(hello).
-export([start/0]).
start() ->
io:format("Hello World~n").
6.3.1 在Erlang shell中编译运行
# 使用c(ModuleName)编译模块
1> c(hello).
{ok,hello}
# 使用Module:Fun(Args)调用某个模块的某个函数
2> hello:start().
Hello World
ok
6.3.2 在命令行提示符下编译运行
# 使用erlc编译模块
$ erlc hello.erl
# -noshell 启动Erlang但关闭shell
# -s hello start 执行hello模块的start函数
# -s init stop 执行init模块的stop函数, 即退出Erlang
$ erl -noshell -s hello start -s init stop
Hello World
# 加载指定路径并执行其所包含的模块中的函数
$ erl -noshell -pa path -s module fun
6.3.3 把程序当做escript脚本运行
#!/usr/bin/env escript
main(_) ->
io:format("Hello World\n").
对文件添加可执行权限后执行, 即可达到与普通Erlang程序相同的结果。
6.3.4 用命令行参数编程
编写一个根据计算给定参数阶乘的程序。
-module(fac).
-export([main/1]).
%% 将参数转换成整数类型后调用阶乘函数
main([A]) ->
I = list_to_integer(atom_to_list(A)),
F = fac(I),
io:format("factorial ~w = ~w~n", [I, F]),
init:stop().
fac(0) ->1;
fac(N) ->N*fac(N-1).
运行结果:
$ erlc fac.erl
$ erl -noshell -s fac main 25
factorial 25 = 15511210043330985984000000
6.4 使用makefile进行自动编译
6.4.1 makefile模版
# 声明涉及到的文件类型
.SUFFIXES: .erl .beam .yrl
# 依赖关系
# .beam文件依赖于.erl文件
.erl.beam:
erlc -W $<
.yrl.erl:
erlc -W $<
# 定义变量
ERL = erl -boot start_clean
# 定义要编译的模块列表, 使用"\"来折行
# Edit the lines below
MODS = module1 module2 \
module3 ... special1 ...\
...
moduleN
# 定义默认的target
all: compile
compile: ${MODS:%=%.beam} subdirs
## 定义target的依赖关系及其执行命令
special1.beam: special1.erl
${ERL} -Dflag1 -W0 special1.erl
## run an application from the makefile
application1: compile
${ERL} -pa Dir1 -s application1 start Arg1 Arg2
# the subdirs target compiles any code in
# sub-directories
subdirs:
cd dir1; make
cd dir2; make
...
# remove all the code
clean:
rm -rf *.beam erl_crash.dump
cd dir1; make clean
cd dir2; make clean
6.4.2 定制makefile模版
.SUFFIXES: .erl .beam
.erl.beam:
erlc -W $<
ERL = erl -boot start_clean
MODS = module1 module2 module3
all: compile
${ERL} -pa 'codepath' -s module1 start
compile: ${MODS:%=%.beam}
clean:
rm -rf *.beam erl_crash.dump
6.5 在Erlang shell中的命令编辑
命令 | 描述 |
---|---|
^A | 一行的开始 |
^E | 一行的结尾 |
^F或右箭头 | 前进一个字符 |
^B或左箭头 | 后退一个字符 |
^P或上箭头 | 前一行 |
^N或下箭头 | 后一行 |
^T | 颠倒两个字母顺序 |
Tab键 | 帮助扩展当前模块或函数名 |
6.6 解决系统死锁
出现死锁的原因:
- shell没有响应
- Ctrl+C处理程序被禁止
- Erlang启动时带有-detached选项
- Erlang启动时带有-heart Cmd选项
- 发生严重错误导致了僵尸进程
6.7 如何应对故障
6.7.1 未定义/遗失代码
- 调用未经编译的模块中的函数
- 调用不在代码搜索路径中的模块中的函数
- 调用存在多个版本的同一模块中的函数
6.7.2 makefile不能工作
- makefile中的空格, 缩进应以tab字符开始
- 缺失要编译的模块文件
6.7.3 shell没有响应
Eshell V5.9.1 (abort with ^G)
# 停止响应后通过Ctrl+G进入JCL模式
# 输入h查看帮助
# 输入s启动新shell
# 输入j查看任务列表
# 输入c nn 切换到指定的shell
1> receive foo -> true end.
User switch command
--> h
c [nn] - connect to job
i [nn] - interrupt job
k [nn] - kill job
j - list all jobs
s [shell] - start local shell
r [node [shell]] - start remote shell
q - quit erlang
? | h - this message
--> j
1* {shell,start,[init]}
--> s
--> j
1 {shell,start,[init]}
2* {shell,start,[]}
--> c 2
Eshell V5.9.1 (abort with ^G)
1> init:stop().
ok
6.8 获取帮助
使用erl -man module 获取帮助
6.9 调试环境
系统自带的命令都包含在shell_default模块中, 如果要自定义命令, 可以创建一个user_default模块, 将其放在加载路径中即可直接使用其中的函数。
6.10 崩溃转储
Erlang崩溃后会产生erl_crash.dump文件, 分析此文件可以使用系统自带的基于Web的崩溃分析器。