【问题标题】:Custom allocation and Boehm GC自定义分配和 Boehm GC
【发布时间】:2011-10-17 20:03:56
【问题描述】:

在我的 on-again-off-again 编译器项目中,我将闭包实现为带有可执行前缀的已分配内存。所以一个闭包是这样分配的:

c = make_closure(code_ptr, env_size, env_data);

c 是一个指向已分配内存块的指针,如下所示:

movl $closure_call, %eax
call *%eax
.align 4
; size of environment
; environment data
; pointer to closure code

closure_call 是一个帮助函数,它查看最近放置在堆栈上的地址,并使用它来查找闭包数据和代码指针。 Boehm GC 用于一般的内存管理,当闭包不再被引用时,它可以被 GC 释放。

无论如何,这个分配的内存需要被标记为可执行;事实上,它跨越的整个页面都被标记了。随着闭包的创建和释放,进程中越来越多的堆内存将是可执行的。

出于防御性编程的原因,我希望尽量减少可执行堆的数量。我的计划是尝试将所有闭包放在同一页面上,并根据需要分配和释放可执行页面;即为闭包实现自定义分配器。 (如果所有闭包的大小相同,这会更容易;因此第一步是将环境数据移动到可以正常管理的单独的不可执行分配中。这也具有防御性编程的意义。)

但剩下的问题是 GC。 Boehm 已经这样做了!我想要以某种方式告诉 Boehm 我的自定义分配,并让 Boehm 告诉我它们何时能够被 GC 处理,但由我自行解除分配。

所以我的问题是,Boehm 中是否有提供这样的自定义分配的钩子?

【问题讨论】:

    标签: compiler-construction garbage-collection closures boehm-gc


    【解决方案1】:

    你可以用finalizer 做你想做的事——Boehm GC 仍然会释放它,但你有机会事先用断点操作(x86 上的 0xCC)memset 闭包并将其页面标记为非如果可能,可执行。

    但是,终结器有性能成本,因此不应轻易使用。 Boehm GC 基于标记扫描算法,该算法首先识别所有应该释放的块(mark.c),然后一次性释放所有其他块(reclaim.c)。在您的情况下,修改回收过程以使用断点操作填充可执行区域中的所有可用空间并在页面完全为空时将它们标记为不可执行是有意义的。这避免了终结器,代价是分叉库(我找不到任何可扩展机制)。

    最后,请注意,执行预防是一种纵深防御措施,不应成为您唯一的安全保护。 Return-oriented programming 可用于使用不可修改的可执行区域执行任意代码。

    【讨论】:

    • 那么,使用终结器方法,其效果是可执行分配将通过堆进行,但是当有释放时,如果可能,它们将返回不可执行?这是一个改进,尽管我们需要一种方法来跟踪该页面上的其他可执行分配,以便我们知道何时重新标记它。断点操作的使用似乎与分配本身有点正交——我猜,在可执行页面上填充可用空间是一种很好的做法。
    • 对。分叉 GC 的一个好处是,在标记阶段,您可以很容易地跟踪哪些页面上有可访问的对象(只需为每个可执行页面使用带有位的位图)。然后,您可以将从未到达的那些标记为不可执行。除了自己浏览参考图之外,我不知道有什么方法可以使用终结器来做任何类似的事情。
    • 我可以维护每个页面上分配的可执行对象的计数。创建对象时递增,在对象的终结器中递减。如果一个对象跨越页面边界,事情会变得有点棘手(只需要增加/减少两个页面)。
    • 听起来不错,是的。请记住,您不一定可以取消映射空页面,因为下一页上块的分配元数据可能落在当前页面上。但是,您应该能够将它们标记为不可执行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多