【发布时间】:2017-09-29 01:35:33
【问题描述】:
为了并发/并行GC, 我对 mprotect 系统调用提供的内存顺序保证感兴趣(即 mprotect 具有多个线程的行为或 mprotect 的内存模型)。我的问题是(假设没有编译器重新排序或有足够的编译器障碍)
如果线程 1 由于 mprotect on 而触发地址上的段错误 线程 2,我可以确定一切都发生在线程 2 之前 系统调用可以在线程 1 的信号处理程序中观察到 段错误?如果在信号中放置了一个完整的内存屏障怎么办 在线程 1 上执行加载之前的处理程序?
-
如果线程 1 对设置为 线程 2 的 PROT_NONE 并且没有触发段错误,这足够了吗 a 发生在两者之间的关系之前。或者换句话说,如果 两个线程都可以(
*ga以0开头,p是一个以只读方式启动的页面对齐地址)// thread 1 *ga = 1; *(volatile int*)p; // no segfault happens // thread 2 mprotect(p, 4096, PROT_NONE); // Or replace 4096 by the real userspace-visible page size a = *ga;是否可以保证线程 2 上的
a将是1? (假设没有 在线程 1 上观察到 segfault 并且没有其他代码修改*ga)
我最感兴趣的是 Linux 行为,尤其是 x86(_64)、arm/aarch64 和 ppc,尽管欢迎访问有关其他 archs/OS 的信息(对于 Windows,将 mprotect 替换为 VirtualProtect 或其他任何名称... .)。到目前为止,我在 x64 和 aarch64 Linux 上的测试表明没有违反这些规定,但我不确定我的测试是否是结论性的,或者是否可以长期依赖这种行为。
一些搜索表明mprotect 可能会在删除权限时对具有映射地址的所有线程发出 TLB 击落,这可能提供此处所述的保证(或者换句话说,提供此保证似乎是此类操作的目标) 虽然我不清楚内核代码的未来优化是否会破坏这一保证。
Ref LKML post 一周前我在哪里问过这个问题,但还没有回复...
编辑:澄清问题。我知道 tlb 击落应该提供我正在寻找的保证,但我想知道是否可以依赖这种行为。换句话说,内核发出此类请求的原因是什么,因为如果不是为了提供某种排序保证,就不需要它。
【问题讨论】:
-
你所说的“在系统调用可以被观察到之前”是什么意思?怎么观察?你是说系统调用的效果吗?如果我们从内核角度来看,系统调用是一个更改虚拟内存属性的请求,其影响将通过翻译缓冲区传播,并且如果多个用户空间线程在不同的内核(或不同的 CPU 上运行,具体取决于在架构上),他们可能会在不同的物理时刻看到效果,除非完成刷新。在 Linux 中,TLB 刷新已完成,除非旧属性不允许访问。 [...]
-
[...] 这意味着行为会有所不同,具体取决于系统调用是允许(
PROT_NONE到PROT_READ)还是不允许(PROT_READ到PROT_NONE)访问。在 Linux 中,前一种情况下省略了 TLB 刷新,因此并发运行的线程可能会在不同时间看到变化。在后一种情况下,TLB 刷新已完成,因此线程应同时观察更改(在 TLB 刷新时)——尽管我不确定是否有硬件在单独的 CPU(包!)上进行 TLB 刷新非同时的。 -
我怀疑没有回复 LKLM 的原因有两个:一,这听起来很抽象,内核开发人员很务实;大多数人都有足够多有趣的现实世界问题来处理,而不太关心没有现实世界影响的抽象问题。第二,很难理解这里的确切情况是什么。一个真实世界的用例——“mm 逻辑是否保证这个场景有效?” -- 使用 ASCII 艺术图显示在同时线程中会发生什么会有很大帮助。
-
@NominalAnimal 观察系统调用意味着由于 mprotect 调用,权限被翻转的页面上的内存操作出错(或不出错)。重新 TLB 刷新,是的,我知道它是在 linux 上完成的,尽管我的问题更多是关于我可以依赖什么行为,或者换句话说,它试图通过发出这样的刷新来维持什么保证。回复:一个真实世界的用例,我正在跳伪代码以防情况2足以开始,似乎不是=(
标签: c multithreading garbage-collection virtual-memory mprotect