第 15 章 运行时系统 (ocamlrun)

ocamlrun 命令执行由 ocamlc 命令的链接阶段生成的字节码文件。

1 概述

ocamlrun 命令包含三个主要部分:字节码解释器,它实际上执行字节码文件;内存分配器和垃圾收集器;以及一组实现诸如输入/输出等基本操作的 C 函数。

ocamlrun 的用法是

        ocamlrun options bytecode-executable arg1 ... argn

第一个非选项参数被认为是包含可执行字节码的文件名。(该文件在可执行路径以及当前目录中被搜索。)其余参数被传递给 OCaml 程序,位于字符串数组 Sys.argv 中。该数组的元素 0 是字节码可执行文件名;元素 1 到 n 是其余参数 arg1argn

如第 ‍13 章所述,由 ocamlc 命令生成的字节码可执行文件是自执行的,并且能够自动在其自身上启动 ocamlrun 命令。也就是说,假设 a.out 是一个字节码可执行文件,那么

        a.out arg1 ... argn

        ocamlrun a.out arg1 ... argn

效果完全相同。

请注意,直接调用 a.out 时,无法向 ocamlrun 传递选项。

Windows:  在几个版本的 Windows 中,字节码可执行文件只有在文件名以 .exe 结尾时才是自执行的。建议始终为字节码可执行文件指定 .exe 名字,例如,用 ocamlc -o myprog.exe ... 而不是 ocamlc -o myprog ... 进行编译。

2 选项

ocamlrun 识别以下命令行选项。
-b
当程序由于未捕获的异常而终止时,打印详细的“回溯”,显示异常是在哪里被抛出的以及此时哪些函数调用是未完成的。只有当字节码可执行文件包含调试信息时才会打印回溯,即是用 -g 选项编译和链接的,该选项设置了 ocamlc。这相当于在 OCAMLRUNPARAM 环境变量中设置 b 标志(参见下文)。
-config
打印 ocamlrun 的版本号和其配置的详细摘要,然后退出。
-I dir
除了标准搜索路径(参见第 ‍15.3 节)之外,还在目录 dir 中搜索动态加载的库。
-m
打印作为参数给出的字节码可执行文件的魔数,然后退出。
-M
打印此版本运行时期望的字节码可执行文件的魔数,然后退出。
-p
打印此版本 ocamlrun 已知的原语名称,然后退出。
-t
增加调试运行时的跟踪级别(否则被忽略)。
-v
指示内存管理器在标准错误流上打印一些进度消息。这相当于在 OCAMLRUNPARAM 环境变量中设置 v=61(参见下文)。
-version
打印版本字符串并退出。
-vnum

打印简短版本号并退出。

还将参考以下环境变量
CAML_LD_LIBRARY_PATH
用于搜索动态加载库的额外目录(参见第 ‍15.3 节)。
OCAMLLIB
包含 OCaml 标准库的目录。(如果未设置 OCAMLLIB,则将使用 CAMLLIB。)用于定位动态加载的 ld.conf 配置文件(参见第 ‍15.3 节)。如果未设置,则默认为编译 OCaml 时指定的库目录。
OCAMLRUNPARAM
设置运行时系统选项和垃圾收集参数。(如果未设置 OCAMLRUNPARAM,则将使用 CAMLRUNPARAM。)此变量必须是参数规范的序列,用逗号分隔。为了方便起见,变量开头的逗号将被忽略,多个逗号将被解释为单个逗号。参数规范是一个选项字母后跟一个 = 符号、一个十进制数(或以 0x 为前缀的十六进制数),以及一个可选的乘数。选项在下面有说明;选项 a, i, l, m, M, n, o, O, s, v, w 对应于 模块 Gc 中记录的 control 记录的字段。
b
(回溯) 当未捕获的异常导致程序终止时,触发打印堆栈回溯。可以提供一个可选参数:b=0 关闭回溯打印;b=1 等同于 b 并打开回溯打印;b=2 打开回溯打印并强制运行时系统在程序启动时而不是在回溯打印时加载调试信息。b=2 可用于运行时在回溯打印时无法加载调试信息的情况,例如没有可用的文件描述符。
c
(cleanup_on_exit) 在退出时优雅地关闭运行时(参见第 ‍22.7.5 节中的 caml_shutdown)。该选项还启用池化(如 caml_startup_pooled)。此模式可用于使用第三方内存调试器检测泄漏。
e
(runtime_events_log_wsize) 每个域运行时事件环形缓冲区的以 2 为底的对数字大小。默认为 16,在 64 位系统上提供 64k 字或 512kb 缓冲区。
l
(stack_limit) 堆栈大小的限制(以字为单位)。这仅与字节码运行时相关,因为原生代码运行时使用操作系统的堆栈。
m
(custom_minor_ratio) 小堆中自定义值所持有的堆外内存的浮动垃圾边界。当小堆中自定义值持有这么多内存时,将触发一次小 GC。以小堆大小的百分比表示。默认值:100。注意:这仅适用于使用 caml_alloc_custom_mem 分配的值。
M
(custom_major_ratio) 大堆中自定义值(例如 bigarrays)所持有的堆外内存的浮动垃圾与大堆大小的目標比率。GC 速度会进行调整,以尝试使用这么多内存来存储尚未收集的死亡值。以大堆大小的百分比表示。默认值:44。注意:这仅适用于使用 caml_alloc_custom_mem 分配的值。
n
(custom_minor_max_size) 在小堆中分配的每个自定义值的堆外内存的最大数量。当在小堆中分配自定义值且该值持有超过这么多字节的内存时,仅该值被计入 custom_minor_ratio,其余部分直接计入 custom_major_ratio。默认值:8192 字节。注意:这仅适用于使用 caml_alloc_custom_mem 分配的值。
乘数为 kMG,分别表示乘以 210、220 和 230
o
(space_overhead) 大 GC 速度设置。有关详细信息,请参阅 Gc 模块文档。
p
(解析器跟踪) 打开对由 ocamlyacc 生成的解析器的调试支持。当此选项打开时,执行解析器的下推自动机将打印其操作的跟踪信息。此选项不接受参数。
R
(随机化) 默认情况下打开所有哈希表的随机化(参见 模块 Hashtbl)。此选项不接受参数。
s
(minor_heap_size) 小堆的大小(以字为单位)。
t
设置调试运行时的跟踪级别(标准运行时忽略)。
v
(verbose) 在 stderr 上打印哪些 GC 消息。这是一个从以下值中选择的值的总和
1 (= 0x001)
大 GC 周期的开始和结束。
2 (= 0x002)
小收集和大 GC 片段。
4 (= 0x004)
堆的增长和缩小。
8 (= 0x008)
堆栈和内存管理器表的调整大小。
16 (= 0x010)
堆压缩。
32 (= 0x020)
GC 参数的更改。
64 (= 0x040)
计算大 GC 片段大小。
128 (= 0x080)
调用终结函数
256 (= 0x100)
启动消息(加载字节码可执行文件、解析共享库)。
512 (= 0x200)
计算压缩触发条件。
1024 (= 0x400)
在程序退出时输出 GC 统计信息。
2048 (= 0x800)
GC 调试消息。
4096 (= 0x1000)
地址空间保留更改。
(verify_heap) 在完成主要垃圾回收周期后,对堆进行完整性检查
W
将运行时警告打印到标准错误流(例如,在文件上打开的通道在未关闭的情况下消失、未刷新的数据等)

如果选项字母无法识别,则整个参数将被忽略;如果等号或数字丢失,则该值将被视为 1;如果乘数无法识别,则会被忽略。

例如,在 32 位机器上,在 bash 中,命令

        export OCAMLRUNPARAM='b,s=256k,v=0x015'

告诉后续的 ocamlrun 打印未捕获异常的回溯,将其初始次要堆大小设置为 1 兆字节,并在每次主要垃圾回收周期开始时、堆大小发生变化时以及触发压缩时打印一条消息。

CAMLRUNPARAM
如果环境中找不到 OCAMLRUNPARAM,则将使用 CAMLRUNPARAM。如果 CAMLRUNPARAM 也找不到,则将使用默认值。
PATH
搜索字节码可执行文件的目录列表。

3 共享库的动态加载

在支持动态加载的平台上,ocamlrun 可以与 C 共享库(DLL)动态链接,这些库提供超出标准运行时系统提供的那些 C 原语。这些库的名称在链接时提供,如第 22.1.4 节所述,并在字节码可执行文件中记录;然后,ocamlrun 定位这些库并解析对它们的原语的引用,当字节码可执行程序启动时。

ocamlrun 命令按以下顺序搜索以下目录中的共享库

  1. ocamlrun 命令行中使用 -I 选项指定的目录。
  2. CAML_LD_LIBRARY_PATH 环境变量中指定的目录。
  3. 通过 -dllpath 选项链接时指定的目录 ocamlc。(这些目录记录在字节码可执行文件中。)
  4. 在文件 ld.conf 中指定的目录。此文件位于 OCaml 标准库目录中,并列出要搜索的目录名称(每行一个)。通常,它只包含一行,命名为 OCaml 标准库目录的 stublibs 子目录。用户可以在其中添加其他目录的名称,这些目录包含常用的共享库;但是,为了安装的一致性,我们建议将共享库直接安装到系统 stublibs 目录中,而不是向 ld.conf 文件添加行。
  5. 系统动态加载器搜索的默认目录。在 Unix 下,这些通常包括 /lib/usr/lib,以及文件 /etc/ld.so.conf 和环境变量 LD_LIBRARY_PATH 中列出的目录。在 Windows 下,这些包括 Windows 系统目录,以及 PATH 环境变量中列出的目录。

4 常见错误

本节描述并解释最常遇到的错误消息。

filename: no such file or directory
如果 filename 是一个自执行字节码文件的名称,这意味着该文件要么不存在,要么它无法在自身上运行 ocamlrun 字节码解释器。第二个可能性表明 OCaml 未在您的系统上正确安装。
无法执行 ocamlrun
(启动自执行字节码文件时。)ocamlrun 在可执行路径中找不到。检查 OCaml 是否已在您的系统上正确安装。
找不到字节码文件
ocamlrun 尝试执行的文件(例如,作为 ocamlrun 的第一个非选项参数给出的文件)要么不存在,要么不是有效的可执行字节码文件。
字节码文件已截断
ocamlrun 尝试执行的文件不是有效的可执行字节码文件。可能是它在创建后被截断或损坏。删除并重新构建它。
未捕获异常
正在执行的程序包含一个“意外”异常。也就是说,它在某一点引发了一个异常,并且此异常从未被捕获。这会导致程序立即终止。异常的名称将被打印出来,以及它的字符串、字节序列和整数参数(更复杂类型的参数不会被正确打印)。要找到未捕获异常的上下文,请使用 -g 选项编译程序,然后在 ocamldebug 调试器下再次运行它(参见第 20 章),或者使用 ocamlrun -b 或将 OCAMLRUNPARAM 环境变量设置为 b=1 来运行它。
内存不足
正在执行的程序需要比可用内存更多的内存。要么程序构建了过大的数据结构;要么程序包含过多的嵌套函数调用,导致堆栈溢出。在某些情况下,您的程序是完全正确的,它只需要比您的机器提供的内存更多的内存。在其他情况下,“内存不足”消息揭示了程序中的错误:非终止递归函数、分配过大的数组、字符串或字节序列、尝试构建无限列表或其他数据结构,……

为了帮助您诊断此错误,请使用 -v 选项运行您的程序 ocamlrun,或者将 OCAMLRUNPARAM 环境变量设置为 v=63。如果它显示很多“Growing stack……”消息,这可能是循环递归函数。如果它显示很多“Growing heap……”消息,并且堆大小缓慢增长,这可能是尝试构建包含太多(无限多?)单元的数据结构。如果它显示很少的“Growing heap……”消息,但堆大小有很大的增量,这可能是尝试构建过大的数组、字符串或字节序列。