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日 无锡

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,192评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,186评论 1 303
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,844评论 0 252
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,471评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,876评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,891评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,068评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,791评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,539评论 1 249
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,772评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,250评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,577评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,244评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,146评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,949评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,995评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,812评论 2 276