是 mmap
在它们的效果中调用原子?
也就是说,是否由 mmap
进行了映射更改以原子方式出现在访问受影响区域的其他线程中?
作为试金石,请考虑您执行 mmap
的情况。在一个全为零的文件中(来自线程 T1,此时是唯一的线程),然后启动从该区域读取的第二个线程 T2。然后,再次在 T1(原始线程)上执行第二个 mmap
调用相同的区域,将映射替换为针对所有区域的文件的新映射。
阅读器线程是否有可能从某个页面读取一个 1(即,参见第二个 mmap
有效),然后随后从某个页面读取一个 0(即,参见第一个有效映射)?
您可以假设读取器线程上的读取被正确隔离,即上述效果不会仅仅由于 CPU/一致性级别的内存访问重新排序而发生。
最佳答案
Mmap(2)
对于跨所有线程的映射是原子的;至少部分是因为 unmap(2)
也是。分解一下,描述的场景类似于:
MapRegion(from, to, obj) {
Lock(&CurProc->map)
while MapIntersect(&CurProc->map, from, to, &range) {
MapUnMap(&CurProc->map, range.from, range.to)
MapObjectRemove(&CurProc->map, range.from, range.to)
}
MapInsert(&CurProcc->map, from, to, obj)
UnLock(&CurProc->map)
}
在此之后,map_unmap
必须确保在删除映射时,没有线程可以访问它们。请注意 Lock(&thisproc->map)
.MapUnMap(map, from, to) {
foreach page in map.mmu[from .. to] {
update page structure to invalidate mapping
}
foreach cpu in map.HasUsed {
cause cpu to invoke tlb cache invalidation for (map, from, to)
}
}
第一阶段是重写处理器特定页表以使区域无效。第二阶段是强制每个曾经将此映射加载到其翻译缓存中的 cpu 使该缓存无效。该位高度依赖于架构。在较旧的 x86 上,重写
cr3
通常就足够了,所以 HasUsed
真的是CurrentlyUsing
;而较新的 amd64 可能能够缓存多个地址空间标识符,HasUsed
也是如此。 .在 ARM 上,本地 tlb 失效被广播到本地集群;所以HasUsed
将引用集群 ID 而不是 CPU 的 ID。更多详情,请搜索 tlb shootdown
,正如俗称的那样。一旦这两个阶段完成,就没有
thread
可以访问这个地址范围。任何这样做的尝试都会导致故障,这将导致 faulting thread
锁定它的映射结构,该结构已经被 mapping thread
锁定,所以它会一直等到映射完成。映射完成后,所有旧映射都已删除并由新映射替换,因此在此之后无法检索先前的映射。如果另一个
thread
在更新期间引用地址范围?它将继续处理陈旧数据或出现故障。在这方面,陈旧数据并不是不一致的,就好像它在 mapping thread
之前被引用了一样。已进入 mmap(2)
.故障情况与 faulting thread
相同以上。总之,对映射的更新是使用一系列确保地址空间一致 View 的事务来实现的。这些交易的成本是特定于架构的。实现这一点的代码可能非常复杂,因为它需要防范隐式操作,例如推测性获取,以及显式操作。
关于linux - mmap 是原子的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59845919/