让 OCaml 值在堆外永远保持活动状态/故意泄漏 OCaml 值

Gre*_*bet 3 c ocaml

要在 C 函数中在堆外分配永久 OCaml 对象,需要做什么?特别是,如何创建一个在运行时看起来像 OCaml 源代码中的全局变量一样的 OCaml 值。

这是我尝试生成一个故意损坏的程序,该程序忽略将一个值注册为 GC 根。

这是驱动一切的 OCaml 源文件。

(* immortal_string.ml *)

external make_string : string -> unit = "make_string"
external get_string : unit -> string = "get_string"

let () = make_string "a"
let () = Gc.full_major ()
let () = Printf.printf "%s\n" (get_string ())
Run Code Online (Sandbox Code Playgroud)

以及C实现。可能有比用作0哨兵值和静态函数更好的方法来做到这一点,但我认为意图很明确。请注意,多次调用make_string将破坏原来存在的值,但没关系。我希望垃圾收集器回收旧值指向的内存。

// lib_immortal_string.c

#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/alloc.h>

value *storage(void) {
    // BAD! we haven't registered this thing
    // as a GC root. No clue how you do that.
    static value data = 0; // sentinel, will never be valid OCaml value
    if (data == 0) {
        data = caml_copy_string("");
    }
    return &data;
}

CAMLprim value
make_string(value ml_string) {
    CAMLparam1(ml_string);
    *storage() = ml_string;
    CAMLreturn(Val_unit);
}

CAMLprim value
get_string(value ml_unit) {
    CAMLparam1(ml_unit);
    CAMLreturn(*storage());
}
Run Code Online (Sandbox Code Playgroud)

我预计这个程序会出现段错误,因为没有任何明显的功能可以保持data活动storage状态。data不是全局的,也不在堆栈上。但是,该程序似乎在没有回收字符串的情况下运行。

$ ocamlopt immortal_string.ml lib_immortal_string.c
./a.out
a
Run Code Online (Sandbox Code Playgroud)

所以我的问题是,制作全局 OCaml 值/堆外事物的正确方法是什么?而且,为什么上面的程序看起来可以工作而不是崩溃?

Jef*_*eld 5

在你的小程序中几乎没有内存的重用,所以我猜字符串“a”在垃圾回收后看起来仍然是一样的,即使它没有被任何东西引用。

如果每次都调用 GC,它只会将事物返回到有序状态。最好让 GC 正常发生,这样会运行更多可能的内存状态。

您还必须执行一些循环,以便有时间失败。

您的代码的稍微修改版本实际上对我来说确实存在段错误:

external make_string : string -> unit = "make_string"
external get_string : unit -> string = "get_string"

let () =
    while true do
        let a = String.make (1024 * 1024) 'a' in
        make_string a;
        let b = String.make (1024 * 1024) 'b' in
        Printf.printf "%s %s\n" (get_string ()) b
    done
Run Code Online (Sandbox Code Playgroud)

将值标记为 GC 根的方法是caml_register_global_root

caml_register_global_root(&data);
Run Code Online (Sandbox Code Playgroud)

正如您所期望的那样,如果我在下面调用它,if (data == 0) {则不会出现段错误。

OCaml 手册第 19.5 节对此进行了记录。