第六章 编译并运行程序

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 解决系统死锁

出现死锁的原因:

  1. shell没有响应
  2. Ctrl+C处理程序被禁止
  3. Erlang启动时带有-detached选项
  4. Erlang启动时带有-heart Cmd选项
  5. 发生严重错误导致了僵尸进程

6.7 如何应对故障

6.7.1 未定义/遗失代码

  1. 调用未经编译的模块中的函数
  2. 调用不在代码搜索路径中的模块中的函数
  3. 调用存在多个版本的同一模块中的函数

6.7.2 makefile不能工作

  1. makefile中的空格, 缩进应以tab字符开始
  2. 缺失要编译的模块文件

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的崩溃分析器。

posted @ 2020-08-20 16:37  养诚  阅读(384)  评论(0编辑  收藏  举报