linux - 如何通过替换为空页面的映射来取消映射 mmap 文件
问题描述
/dev/null
Linux用户空间有没有办法用空页面(映射自,或者可能是单个空页面,在页面顶部重复映射)替换映射文件的页面(或某个逻辑地址范围内的mmap页面)从文件映射)?
对于上下文,我想找到此 JDK 错误的修复程序:
https://bugs.openjdk.java.net/browse/JDK-4724038
总结一下这个错误:在 JVM 可以垃圾收集包装 mmap 文件的文件之前,目前不可能在 Java 中取消映射文件MappedByteBuffer
,因为强制取消映射文件可能会由于竞争条件而导致安全问题(例如,本机代码可能仍在尝试访问文件映射到的相同地址范围,并且操作系统可能已经将新文件映射到相同的逻辑地址范围)。
我正在寻找替换逻辑地址范围内的映射页面,然后取消映射文件。有没有办法做到这一点?
(如果您也知道在其他操作系统中执行此操作的方法,尤其是 Windows 和 Mac OS X,则可以加分。)
请注意,这不一定是原子操作。主要目标是将内存的取消映射(或将映射的文件内容替换为读取时为零的页面)与文件的关闭分开,因为这将解决 Linux 上的一系列问题(具有每个进程的文件描述符数量的下限)和 Windows(在映射文件时不能删除文件的事实)。
解决方案
错误在 JDK 中保留这么久的原因基本上是因为取消映射内存和映射虚拟内存之间的竞争条件,一些其他内存最终可能会映射到那里(可能是本机代码)。我已经研究过操作系统 API,并且在系统调用级别不存在原子的内存操作,这些操作取消映射文件并将其他内容映射到同一地址。但是,有些解决方案会阻止整个过程,同时从其下方换出映射。
unmap 在没有保护的情况下在 finalize 中正常工作,因为 GC 首先证明了该对象是不可访问的,因此没有竞争。
高度 Linux 特定的解决方案:
1) vfork()
2)向父母发送停止信号
3)取消映射内存
4)在其位置映射零
5) 向父母发送一个 CONT 信号
6) _exit (解除对父线程的阻塞)
在 Linux 中,内存映射更改会传播到父代。
代码实际上看起来更像这样(vfork()
是疯子):
int unmap(void *addr, int length)
{
int wstatus;
pid_t child;
pid_t parent;
int thread_cancel_state;
signal_set signal_set;
signal_set old_signal_set;
parent = getpid();
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &thread_cancel_state);
sigfillset(&signal_set);
pthread_sigmask(SIG_SETMASK, &signal_set, &old_signal_set);
if (0 == (child = vfork()) {
int err = 0;
kill(parent, SIGSTOP);
if (-1 == munmap(addr, length))
err = 1;
else if ((void*)-1 == mmap(addr, length, PROT_NONE, MAP_ANONYMOUS, -1, 0);
err = 1;
kill(parent, SIGCONT);
_exit(err);
}
if (child > 0)
waitpid(child, &wstatus, 0);
else
wstatus = 255;
pthread_sigmask(SIG_SETMASK, &old_signal_set, &signal_set);
pthread_setcancelstate(thread_cancel_state, &thread_cancel_state);
return (wstatus & 255) != 0;
}
在 Windows 下,您可以停止所有线程,但这个使用SuspendThread
感觉是为此量身定制的。但是,枚举线程会很困难,因为您正在与CreateThread
. 您必须运行枚举线程ntdll.dll
API(相信我,您不能在这里使用 ToolHelp)以及SuspendThread
除您自己之外的每一个,小心地仅使用 VirtualAlloc 分配内存,因为SuspendThread
刚刚破坏了所有堆分配例程,您将不得不做所有在一个循环中,直到你找不到更多。
这里有一些文章,我觉得我不能准确地提炼出来:
我没有找到任何适用于 Mac OSX 的解决方案。
推荐阅读
- javascript - 为什么从无类型数组到 Uint8ClampedArray 的转换这么慢?
- django - 在 django 中使用 Tweepy 进行身份验证并保存用户的访问令牌和访问令牌秘密
- html - 修复了溢出视口时模态不会滚动的问题
- dart - 导入“包:using_tabs/tabs/first.dart”;>> URI 的目标不存在
- r - R - Boxplot x 轴不带刻度且完整
- javascript - iframe src 属性不起作用
- css - 使用 flex 垂直对齐项目
- java - 如何从 void 方法传输变量?
- symfony - nelmioApiDocBundle 是否支持 symfony 序列化规范化器和名称转换器?
- angular - Angular 应用程序部署到 firebase:404 错误