Erlang是一門適用于開發(fā)大規(guī)脑赴椋可擴(kuò)展實(shí)時(shí)軟件系統(tǒng)的語言,主打 并發(fā) 和 分布式 編程呵扛,在電信、銀行筐带、電商今穿、即時(shí)通信等領(lǐng)域均有應(yīng)用案例。
OTP全稱Open Telecom Platform(開放電信平臺)伦籍,相當(dāng)于Erlang的編譯器工具鏈蓝晒。它提供Erlang程序運(yùn)行的虛擬機(jī),以及類似Python shell的Erlang shell帖鸦。
當(dāng)前版本:OTP 23.0
源碼主要文件:
文件類型 | 文件數(shù)量(含test) | 文件數(shù)量(不含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
是運(yùn)行于虛擬機(jī)上的目標(biāo)文件洛二。
比起編譯器,OTP更像一個(gè)由C/C++實(shí)現(xiàn)的應(yīng)用系統(tǒng)攻锰。即使在config.guess
之外的處理器上也可以直接構(gòu)建晾嘶,只要該平臺具備下列工具。
三方依賴
- GNU make
- gcc or clang
- Perl 5
- GNU m4(用于支持HiPE娶吞,可使用
--disable-hipe
選項(xiàng)關(guān)閉) - ncurses, termcap, or termlib(可使用
--without-termcap
選項(xiàng)關(guān)閉) - sed
- GNU autoconf(使用Git時(shí)需要)
以下工具在缺少時(shí)會自動跳過相關(guān)模塊的編譯:
- OpenSSL 0.9.8(構(gòu)建crypto模塊時(shí)需要)
- JDK 1.6.0(構(gòu)建jinterface模塊時(shí)需要)
- flex(構(gòu)建megaco模塊時(shí)需要)
- wxWidgets 3.0(構(gòu)建wx模塊時(shí)需要)
構(gòu)建文檔時(shí)需要:
- xsltproc
- fop
交叉編譯
在構(gòu)建平臺上垒迂,使用--host
(運(yùn)行平臺)和--build
(構(gòu)建平臺)參數(shù)即可實(shí)現(xiàn)交叉編譯:
$ ./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
文件提供了配置模板妒蛇,基于此創(chuàng)建新架構(gòu)的配置文件erl-xcomp-<UNAME_MACHINE>-linux.conf
机断,其中的erl_xcomp_build
和erl_xcomp_host
分別對應(yīng)命令行中的--build
和--host
,在erl_xcomp_configure_flags
中配置其他命令行參數(shù)绣夺,使用CC
吏奸、CXX
、LD
等選項(xiàng)配置交叉編譯器乐导。參考其他架構(gòu)進(jìn)行配置苦丁。
boostrap配置:
$ ./otp_build configure --xcomp-conf=xcomp/erl-xcomp-<UNAME_MACHINE>-linux.conf
構(gòu)建:
$ ./otp_build boot -a
發(fā)布:
$ ./otp_build release -a <ABSOLUTE_RELEASE_DIR>
<ABSOLUTE_RELEASE_DIR>
為以絕對路徑指定的文件夾,如果使用相對路徑物臂,腳本將在lib
各個(gè)功能模塊的子目錄下分別創(chuàng)建<RELEASE_DIR>
旺拉,唯獨(dú)不在當(dāng)前目錄創(chuàng)建产上,不知意欲何為。
將<ABSOLUTE_RELEASE_DIR>
拷至運(yùn)行平臺蛾狗,cd
進(jìn)去安裝:
$ ./Install [-minimal|-sasl] <ABSOLUTE_INSTALL_DIR>
本地編譯
我們可以直接在運(yùn)行平臺上進(jìn)行編譯:
$ ./configure --build=<UNAME_MACHINE>-unknown-linux-gnu
如果不使用--build
晋涣,就需要在各config.guess
和config.sub
中逐一添加我們的架構(gòu)。
Hello world!
使用一個(gè)簡單示例驗(yàn)證一下新平臺上的OTP能否正常工作沉桌。
編輯源文件:
$ vi hello.erl
-module(hello).
-export([main/0]).
main() ->
io:format("Hello world!~n").
編譯:
$ erlc hello.erl
運(yùn)行:
$ erl -noshell -s hello main -s init stop
Hello world!
架構(gòu)相關(guān)
如上谢鹊,我們不費(fèi)吹灰之力就實(shí)現(xiàn)了OTP到新平臺的「移植」,但其實(shí)OTP源碼并非完全架構(gòu)無關(guān)留凭,以下架構(gòu)相關(guān)部分對OTP的影響待考佃扼。
以mips為例,內(nèi)容包含關(guān)鍵詞的文件共13個(gè):
$ 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
名稱包含關(guān)鍵詞的文件僅交叉編譯配置文件1個(gè):
$ find -iname *mips*
./xcomp/erl-xcomp-mips-linux.conf
除了erts/include/internal/gcc/ethread.h
中的一處#if defined
蔼夜,基本只涉及腳本兼耀、配置、文檔和注釋求冷,少得可憐瘤运。然而,事情沒有那么簡單匠题。
第一處
搜索其他架構(gòu)后發(fā)現(xiàn)拯坟,盡管OTP在主流平臺上通用,但深度適配的只有x86韭山、powerpc郁季、sparc、tilera掠哥,這在定義了多線程巩踏、原子操作、內(nèi)存屏障续搀、讀寫鎖塞琼、自旋鎖等內(nèi)核對象的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
中涉及一些操作系統(tǒng)內(nèi)核相關(guān)的定義(以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__))) */
第三處
最后還有一塊架構(gòu)相關(guān)的大頭就是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程序設(shè)計(jì)(第2版)[M]. 疟肷迹化成, 譯. 北京: 人民郵電出版社, 2014.
2020年8月27日~9月2日 無錫