有没有办法在多线程程序中 JIT 汇编器的不同调用之间共享页面?
我正在构建一个小的 JIT 引擎。JITting 背后的基本原理似乎很简单:mmap
一些空间,将机器代码放入其中,mprotect
使其可执行,将代码块的开头转换为函数指针,这似乎就是我所需要的工作系统。
但是,mmap
/mprotect
组合(和 Windows 等效项)仅适用于整个页面(这是对的吗?如果不是,它会抛出整个问题)。这意味着每次 JIT 汇编器运行时,它必须分配至少一整页的空间。对于一次性处理多个大型函数甚至整个程序的 JIT 来说,这没什么大不了的,但我主要对处理非常短的代码片段感兴趣。大多数分配的空间不会被生成的代码使用。
在单线程应用程序中,这不是问题。当轮到 JITter 进行调整时,它可以mprotect
在知道代码当时没有运行的情况下,将代码缓冲到它的核心内容。因此,该空间可以继续用于后续功能,直到它实际上已满。不幸的是,假设或要求单线程现在似乎有点上世纪了。
在多线程应用程序中,动态创建的代码可能在生成其他代码的同时已经在运行。如果 JITter 尝试使用保护,它会破坏某些东西。处理这种情况有哪些选择?
到目前为止我考虑过的事情:
忽略该问题并为每个过程分配一个新缓冲区。实际上,需要数千次分配才能导致内存使用量显着增加,这可能在桌面上运行良好。然而,从哲学上讲,说“去死,我们有很多硬件”和“没有人会强调这个系统”从设计的角度来看有点令人反感。
分配具有写+执行保护的缓冲区。这似乎是显而易见的答案,但它让我有一种不好的感觉,我没有经验来证明这一点(这是否比拥有 JIT 或
mprotect
首先调用 的程序更糟糕?)。安全吗?让每个 JITted 函数尝试锁定其代码缓冲区。这(直观地)似乎可能会降低性能,增加代码大小,并且如果 JITter 天真地完成,可能会永久阻止 JITter 做任何事情。
让 JITter 随意优先访问代码缓冲区和
mprotect
它们,如果这导致任何现有的 JITted 过程发生段错误,则在信号处理程序中捕获它们并等待 JITter 完成。我不确定这实际上是否可行,但是……嗯,这是一个想法。
我已经尝试查看几个现有 JIT 编译器的来源,但在大多数情况下,它们是庞大的系统,需要很长时间才能理解,而且我不明白,而不是带有明确答案的小例子(并不是我不愿意阅读源代码,我只是无法从 JavaScriptCore 或 V8 获得所需的信息;绝对欢迎使用更简单的示例)。无论如何,对于某些项目来说,这种事情似乎实际上是一个悬而未决的问题。