Erlang是一门适用于开发大规模可扩展实时软件系统的语言,主打 并发 和 分布式 编程,在电信、银行、电商、即时通信等领域均有应用案例。
OTP全称Open Telecom Platform(开放电信平台),相当于Erlang的编译器工具链。它提供Erlang程序运行的虚拟机,以及类似Python shell的Erlang shell。
当前版本:OTP 23.0
源码主要文件:
文件类型 | 文件数量(含test) | 文件数量(不含test) |
---|---|---|
.c | 598 | 426 |
.h | 341 | 319 |
.cpp | 11 | 11 |
.cc | 1 | 1 |
.S | 36 | 6 |
.erl | 3950 | 1563 |
.hrl | 310 | 229 |
.beam | 1313 | 1250 |
其中,.erl
是Erlang源文件,hrl
是Erlang头文件,.beam
是运行于虚拟机上的目标文件。
比起编译器,OTP更像一个由C/C++实现的应用系统。即使在config.guess
之外的处理器上也可以直接构建,只要该平台具备下列工具。
三方依赖
- GNU make
- gcc or clang
- Perl 5
- GNU m4(用于支持HiPE,可使用
--disable-hipe
选项关闭) - ncurses, termcap, or termlib(可使用
--without-termcap
选项关闭) - sed
- GNU autoconf(使用Git时需要)
以下工具在缺少时会自动跳过相关模块的编译:
- OpenSSL 0.9.8(构建crypto模块时需要)
- JDK 1.6.0(构建jinterface模块时需要)
- flex(构建megaco模块时需要)
- wxWidgets 3.0(构建wx模块时需要)
构建文档时需要:
- xsltproc
- fop
交叉编译
在构建平台上,使用--host
(运行平台)和--build
(构建平台)参数即可实现交叉编译:
$ ./otp_build configure --host=<UNAME_MACHINE>-unknown-linux-gnu --build=guess
不过OTP提供了更方便的配置文件法,配置文件位于xcomp
目录:
$ ls xcomp/
README.md erl-xcomp-arm64-android.conf erl-xcomp-powerpc-dso-linux-gnu.conf erl-xcomp.conf.template
erl-xcomp-TileraMDE2.0-tilepro.conf erl-xcomp-armv8-rpi3-linux-gnueabihf.conf erl-xcomp-powerpc64-bgq-linux.conf
erl-xcomp-arm-android.conf erl-xcomp-avr32-atmel-linux-gnu.conf erl-xcomp-vars.sh
erl-xcomp-arm-linux.conf erl-xcomp-mips-linux.conf erl-xcomp-x86_64-saf-linux-gnu.conf
xcomp/erl-xcomp.conf.template
文件提供了配置模板,基于此创建新架构的配置文件erl-xcomp-<UNAME_MACHINE>-linux.conf
,其中的erl_xcomp_build
和erl_xcomp_host
分别对应命令行中的--build
和--host
,在erl_xcomp_configure_flags
中配置其他命令行参数,使用CC
、CXX
、LD
等选项配置交叉编译器。参考其他架构进行配置。
boostrap配置:
$ ./otp_build configure --xcomp-conf=xcomp/erl-xcomp-<UNAME_MACHINE>-linux.conf
构建:
$ ./otp_build boot -a
发布:
$ ./otp_build release -a <ABSOLUTE_RELEASE_DIR>
<ABSOLUTE_RELEASE_DIR>
为以绝对路径指定的文件夹,如果使用相对路径,脚本将在lib
各个功能模块的子目录下分别创建<RELEASE_DIR>
,唯独不在当前目录创建,不知意欲何为。
将<ABSOLUTE_RELEASE_DIR>
拷至运行平台,cd
进去安装:
$ ./Install [-minimal|-sasl] <ABSOLUTE_INSTALL_DIR>
本地编译
我们可以直接在运行平台上进行编译:
$ ./configure --build=<UNAME_MACHINE>-unknown-linux-gnu
如果不使用--build
,就需要在各config.guess
和config.sub
中逐一添加我们的架构。
Hello world!
使用一个简单示例验证一下新平台上的OTP能否正常工作。
编辑源文件:
$ vi hello.erl
-module(hello).
-export([main/0]).
main() ->
io:format("Hello world!~n").
编译:
$ erlc hello.erl
运行:
$ erl -noshell -s hello main -s init stop
Hello world!
架构相关
如上,我们不费吹灰之力就实现了OTP到新平台的「移植」,但其实OTP源码并非完全架构无关,以下架构相关部分对OTP的影响待考。
以mips为例,内容包含关键词的文件共13个:
$ grep -lir mips --exclude-dir=*test*
erts/autoconf/config.guess
erts/autoconf/config.sub
erts/configure
erts/doc/src/notes.xml
erts/emulator/hipe/elf64ppc.x
erts/include/internal/gcc/ethread.h
lib/erl_interface/configure
lib/erl_interface/src/auxdir/config.guess
lib/erl_interface/src/auxdir/config.sub
lib/odbc/configure
lib/wx/autoconf/config.guess
lib/wx/autoconf/config.sub
xcomp/erl-xcomp-mips-linux.conf
名称包含关键词的文件仅交叉编译配置文件1个:
$ find -iname *mips*
./xcomp/erl-xcomp-mips-linux.conf
除了erts/include/internal/gcc/ethread.h
中的一处#if defined
,基本只涉及脚本、配置、文档和注释,少得可怜。然而,事情没有那么简单。
第一处
搜索其他架构后发现,尽管OTP在主流平台上通用,但深度适配的只有x86、powerpc、sparc、tilera,这在定义了多线程、原子操作、内存屏障、读写锁、自旋锁等内核对象的erts/include/internal
目录中可见一斑:
$ ls erts/include/internal/
README erl_misc_utils.h erts_internal.mk.in ethr_mutex.h ethread.mk.in gcc ppc32 sparc64 x86_64
erl_errno.h erl_printf.h ethr_atomics.h ethr_optimized_fallbacks.h ethread_header_config.h.in i386 pthread tile
erl_memory_trace_protocol.h erl_printf_format.h ethr_internal.h ethread.h ethread_inline.h libatomic_ops sparc32 win
$ cd erts/include/internal/
$ ls gcc i386 ppc32 sparc32 sparc64 tile x86_64
gcc:
ethr_atomic.h ethr_dw_atomic.h ethr_membar.h ethread.h
i386:
atomic.h ethr_dw_atomic.h ethr_membar.h ethread.h rwlock.h spinlock.h
ppc32:
atomic.h ethr_membar.h ethread.h rwlock.h spinlock.h
sparc32:
atomic.h ethr_membar.h ethread.h rwlock.h spinlock.h
sparc64:
ethread.h
tile:
atomic.h ethr_membar.h ethread.h
x86_64:
ethread.h
第二处
此外,erts/emulator/sys/unix
中涉及一些操作系统内核相关的定义(以powerpc为例):
$ grep -r powerpc erts/emulator/sys/
erts/emulator/sys/unix/erl_unix_sys.h:# elif (defined(__powerpc__) || defined(__ppc__)) && defined(__GNUC__)
erts/emulator/sys/unix/sys_float.c:#elif (defined(__powerpc__) && defined(__linux__)) || (defined(__ppc__) && defined(__DARWIN__))
erts/emulator/sys/unix/sys_float.c:#else /* !(x86 || (sparc && linux) || (powerpc && (linux || darwin))) */
erts/emulator/sys/unix/sys_float.c:#if (defined(__linux__) && (defined(__i386__) || defined(__x86_64__) || defined(__sparc__) || defined(__powerpc__))) || (defined(__DARWIN__) && (defined(__i386__) || defined(__x86_64__) || defined(__ppc__))) || (defined(__FreeBSD__) && (defined(__x86_64__) || defined(__i386__))) || ((defined(__NetBSD__) || defined(__OpenBSD__)) && defined(__x86_64__)) || (defined(__sun__) && defined(__x86_64__))
erts/emulator/sys/unix/sys_float.c:#elif defined(__linux__) && defined(__powerpc__)
erts/emulator/sys/unix/sys_float.c:#if defined(__powerpc64__)
erts/emulator/sys/unix/sys_float.c:#else /* !((__linux__ && (__i386__ || __x86_64__ || __powerpc__)) || (__DARWIN__ && (__i386__ || __x86_64__ || __ppc__))) */
erts/emulator/sys/unix/sys_float.c:#endif /* (__linux__ && (__i386__ || __x86_64__ || __powerpc__)) || (__DARWIN__ && (__i386__ || __x86_64__ || __ppc__))) */
第三处
最后还有一块架构相关的大头就是HiPE(High-Performance Erlang),代码主要集中在lib/hipe
和erts/emulator/hipe
下:
$ ls lib/hipe/
Makefile amd64 boot_ebin doc flow info main native.mk ppc rtl ssa tools vsn.mk
TODO arm cerl ebin icode llvm misc opt regalloc sparc test util x86
$ cd lib/hipe/
$ ls amd64 arm ppc sparc x86
amd64:
Makefile hipe_amd64_frame.erl hipe_amd64_ra.erl hipe_amd64_ra_postconditions.erl hipe_amd64_sse2.erl
hipe_amd64_assemble.erl hipe_amd64_liveness.erl hipe_amd64_ra_finalise.erl hipe_amd64_ra_sse2_postconditions.erl hipe_amd64_subst.erl
hipe_amd64_defuse.erl hipe_amd64_main.erl hipe_amd64_ra_ls.erl hipe_amd64_registers.erl hipe_amd64_x87.erl
hipe_amd64_encode.erl hipe_amd64_pp.erl hipe_amd64_ra_naive.erl hipe_amd64_spill_restore.erl hipe_rtl_to_amd64.erl
arm:
Makefile hipe_arm.hrl hipe_arm_defuse.erl hipe_arm_frame.erl hipe_arm_pp.erl hipe_arm_ra_ls.erl hipe_arm_registers.erl
TODO hipe_arm_assemble.erl hipe_arm_encode.erl hipe_arm_liveness_gpr.erl hipe_arm_ra.erl hipe_arm_ra_naive.erl hipe_arm_subst.erl
hipe_arm.erl hipe_arm_cfg.erl hipe_arm_finalise.erl hipe_arm_main.erl hipe_arm_ra_finalise.erl hipe_arm_ra_postconditions.erl hipe_rtl_to_arm.erl
ppc:
Makefile hipe_ppc_cfg.erl hipe_ppc_frame.erl hipe_ppc_main.erl hipe_ppc_ra_ls.erl hipe_ppc_registers.erl
hipe_ppc.erl hipe_ppc_defuse.erl hipe_ppc_liveness_all.erl hipe_ppc_pp.erl hipe_ppc_ra_naive.erl hipe_ppc_subst.erl
hipe_ppc.hrl hipe_ppc_encode.erl hipe_ppc_liveness_fpr.erl hipe_ppc_ra.erl hipe_ppc_ra_postconditions.erl hipe_rtl_to_ppc.erl
hipe_ppc_assemble.erl hipe_ppc_finalise.erl hipe_ppc_liveness_gpr.erl hipe_ppc_ra_finalise.erl hipe_ppc_ra_postconditions_fp.erl
sparc:
Makefile hipe_sparc_cfg.erl hipe_sparc_liveness_all.erl hipe_sparc_ra.erl hipe_sparc_ra_postconditions_fp.erl
hipe_rtl_to_sparc.erl hipe_sparc_defuse.erl hipe_sparc_liveness_fpr.erl hipe_sparc_ra_finalise.erl hipe_sparc_registers.erl
hipe_sparc.erl hipe_sparc_encode.erl hipe_sparc_liveness_gpr.erl hipe_sparc_ra_ls.erl hipe_sparc_subst.erl
hipe_sparc.hrl hipe_sparc_finalise.erl hipe_sparc_main.erl hipe_sparc_ra_naive.erl
hipe_sparc_assemble.erl hipe_sparc_frame.erl hipe_sparc_pp.erl hipe_sparc_ra_postconditions.erl
x86:
Makefile hipe_rtl_to_x86.erl hipe_x86_cfg.erl hipe_x86_frame.erl hipe_x86_pp.erl hipe_x86_ra_naive.erl hipe_x86_subst.erl
NOTES.OPTIM hipe_x86.erl hipe_x86_defuse.erl hipe_x86_liveness.erl hipe_x86_ra.erl hipe_x86_ra_postconditions.erl hipe_x86_x87.erl
NOTES.RA hipe_x86.hrl hipe_x86_encode.erl hipe_x86_main.erl hipe_x86_ra_finalise.erl hipe_x86_registers.erl
TODO hipe_x86_assemble.erl hipe_x86_encode.txt hipe_x86_postpass.erl hipe_x86_ra_ls.erl hipe_x86_spill_restore.erl
$ ls erts/emulator/hipe/
TODO hipe_amd64_primops.h hipe_bif0.c hipe_debug.c hipe_module.h hipe_ppc_glue.S hipe_sparc_abi.txt hipe_x86_abi.txt
elf64ppc.x hipe_arch.h hipe_bif0.h hipe_debug.h hipe_native_bif.c hipe_ppc_glue.h hipe_sparc_asm.m4 hipe_x86_asm.m4
hipe_abi.txt hipe_arm.c hipe_bif0.tab hipe_gbif_list.h hipe_native_bif.h hipe_ppc_primops.h hipe_sparc_bifs.m4 hipe_x86_bifs.m4
hipe_amd64.c hipe_arm.h hipe_bif1.c hipe_gc.c hipe_ops.tab hipe_primops.h hipe_sparc_gc.h hipe_x86_gc.h
hipe_amd64.h hipe_arm.tab hipe_bif1.h hipe_gc.h hipe_ppc.c hipe_process.h hipe_sparc_glue.S hipe_x86_glue.S
hipe_amd64.tab hipe_arm_abi.txt hipe_bif1.tab hipe_instrs.tab hipe_ppc.h hipe_risc_gc.h hipe_sparc_glue.h hipe_x86_glue.h
hipe_amd64_abi.txt hipe_arm_asm.m4 hipe_bif2.c hipe_load.c hipe_ppc.tab hipe_risc_glue.h hipe_sparc_primops.h hipe_x86_primops.h
hipe_amd64_asm.m4 hipe_arm_bifs.m4 hipe_bif2.tab hipe_load.h hipe_ppc64.tab hipe_risc_stack.c hipe_stack.c hipe_x86_signal.c
hipe_amd64_bifs.m4 hipe_arm_gc.h hipe_bif64.c hipe_mkliterals.c hipe_ppc_abi.txt hipe_signal.h hipe_stack.h hipe_x86_stack.c
hipe_amd64_gc.h hipe_arm_glue.S hipe_bif64.h hipe_mode_switch.c hipe_ppc_asm.m4 hipe_sparc.c hipe_x86.c
hipe_amd64_glue.S hipe_arm_glue.h hipe_bif64.tab hipe_mode_switch.h hipe_ppc_bifs.m4 hipe_sparc.h hipe_x86.h
hipe_amd64_glue.h hipe_arm_primops.h hipe_bif_list.m4 hipe_module.c hipe_ppc_gc.h hipe_sparc.tab hipe_x86.tab
参考资料
- Erlang/OTP System Documentation
- INSTALL.md
- INSTALL-CROSS.md
- Armstrong J. Erlang程序设计(第2版)[M]. 牛化成, 译. 北京: 人民邮电出版社, 2014.
2020年8月27日~9月2日 无锡