如何通过替换为空白页面来取消映射mmap文件

时间:2019-01-23 23:55:28

标签: linux memory-management mmap virtual-memory

Linux用户空间中是否有一种方法可以用空页面(从/dev/null映射到一个空页面,或者替换一个空页面来替换映射文件的页面(或某个逻辑地址范围内的mmap页面),重复地映射到从文件映射的页面顶部)?

对于上下文,我想找到此JDK错误的修复程序:

https://bugs.openjdk.java.net/browse/JDK-4724038

总结一下该错误:在JVM可以垃圾回收包装了mmap文件的MappedByteBuffer之前,当前无法在Java中取消映射文件,因为强行取消映射文件可能会引起安全问题竞争条件(例如,本机代码可能仍试图访问文件映射到的相同地址范围,并且操作系统可能已经将新文件映射到相同的逻辑地址范围)。

我正在寻找替换逻辑地址范围内的映射页面,然后取消映射文件。有什么办法可以做到这一点?

(如果您也知道在其他操作系统(尤其是Windows和Mac OS X)中执行此操作的方法,则将获得奖励。)

请注意,这不一定是原子操作。主要目的是将内存的取消映射(或用零读页面替换映射的文件内容)与关闭文件分开,因为这将解决两个Linux上的一连串问题。 Windows和Windows(每个文件在映射时无法删除的事实)的最低限制。

更新:另请参见:Memory-mapping a file in Windows with SHARE attribute (so file is not locked against deletion)

2 个答案:

答案 0 :(得分:1)

该错误在JDK中保留这么长时间的原因基本上是由于取消映射内存与映射虚拟内存之间的竞争状况,某些其他内存最终可能会映射到那里(可能由本机代码映射)。我已经研究过OS API,并且在syscall级别上没有原子操作会取消映射文件并将其他内容映射到同一地址。但是,有些解决方案会阻塞整个过程,同时从其下面交换出映射。

由于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,但每个API都是您自己的,请小心使用VirtualAlloc分配内存,因为SuspendThread破坏了所有堆分配例程,那么您将必须循环执行所有操作,直到发现更多为止。

这里有些文章我不太觉得我可以准确地提炼出来:

http://forums.codeguru.com/showthread.php?200588-How-to-enumerate-threads-in-currently-running-process

我没有找到Mac OSX的任何解决方案。

答案 1 :(得分:1)

在Linux上,您可以将import { isLoggedIn } from "./store/action/authAction"; import axios from 'axios'; class AllRoutes extends React.Component { componentDidMount() { isLoggedIn(); } render() { console.log(this.props.isAuthenticate); return ( <Router> <Switch> <Route path="/auth/signup" component={Signup} /> <Route path="/auth/signin" component={Signin} /> <Route path="/auth/forgot" component={Forgot} /> <Route exact path="/stock" component={Products} /> <Route exact path="/stock/detail/:id" component={Manage} /> <ProtectedHomeRoute exact path="/" component={<Home />} isAuthenticate={this.props.isAuthenticate} /> </Switch> </Router> ); } } // to access redux stats as props in this component function mapStateToProps(state) { return { isAuthenticate: state.authReducer.isAuthenticate }; } function mapDispatchToProps(dispatch) { return { userAuthenticate: a => { dispatch(isLoggedIn(a)); } }; } export default connect( mapStateToProps, mapDispatchToProps )(AllRoutes); // protected route for Home component const ProtectedHomeRoute = ({ component, isAuthenticate, ...rest }) => { console.log("protected route", isAuthenticate); return ( <Route {...rest} render={props => isAuthenticate ? component : <Redirect to="/auth/signin" /> } /> ); }; mmap一起使用,以将映射替换为所需的任何映射。如果替换整个映射,则对文件的引用将被删除。