Erlang/OTP 新架構移植探索

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_builderl_xcomp_host分別對應命令行中的--build--host,在erl_xcomp_configure_flags中配置其他命令行參數,使用CCCXXLD等選項配置交叉編譯器。參考其他架構進行配置。

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.guessconfig.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/hipeerts/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

參考資料



2020年8月27日~9月2日 無錫

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。