如何创建和写入内存映射文件? [英] How to create and write to memory mapped files?

查看:410
本文介绍了如何创建和写入内存映射文件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编者注:此代码示例来自1.0之前的Rust版本,并且它使用的代码在Rust 1.0中不存在.更新了一些答案,以回答更新版本的Rust的核心问题.

Editor's note: This code example is from a version of Rust prior to 1.0 and the code it uses does not exist in Rust 1.0. Some answers have been updated to answer the core question for newer versions of Rust.

我正在尝试使用std::os::MemoryMap创建一个内存映射文件.当前的方法如下:

I'm trying to create a memory mapped file using std::os::MemoryMap. The current approach looks as follows:

use std::os;
use std::ptr;
use std::old_io as io;
use std::os::unix::prelude::AsRawFd;
use std::os::MapOption;

let path = Path::new("test.mmap");

let f = match io::File::open_mode(&path, io::Open, io::ReadWrite) {
    Ok(f) => f,
    Err(err) => panic!("Could not open file: {}", err),
};

let mmap_opts = &[
    MapOption::MapReadable,
    MapOption::MapWritable,
    MapOption::MapFd(f.as_raw_fd())
];

let mmap = match os::MemoryMap::new(1024*1024, mmap_opts) {
    Ok(mmap) => {
        println!("Successfully created the mmap: {}", mmap.len());
        mmap
    }
    Err(err) => panic!("Could not read the mmap: {}", err),
};

unsafe {
   let data = mmap.data();

    if data.is_null() {
        panic!("Could not access data from memory mapped file")
    }

    let src = "Hello!";
    ptr::copy_memory(data, src.as_ptr(), src.as_bytes().len());
}

该程序失败

Process didn't exit successfully: `target/mmap` (status=4)

在调用ptr::copy_memory或对数据进行任何其他操作时.

when calling ptr::copy_memory or any other operations on data.

  • 我不能从MemoryMap写入(或读取)数据的原因是什么?
  • 在Rust中使用MemoryMap的正确方法是什么?
  • What is the reason I cannot write (or read) the data from the MemoryMap?
  • What is the correct way to use MemoryMap in Rust?

推荐答案

真正的答案是使用可提供此功能的板条箱,最好以跨平台方式提供.

The real answer is to use a crate that provides this functionality, ideally in a cross-platform manner.

use memmap; // 0.7.0
use std::{
    fs::OpenOptions,
    io::{Seek, SeekFrom, Write},
};

const SIZE: u64 = 1024 * 1024;

fn main() {
    let src = "Hello!";

    let mut f = OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open("test.mmap")
        .expect("Unable to open file");

    // Allocate space in the file first
    f.seek(SeekFrom::Start(SIZE)).unwrap();
    f.write_all(&[0]).unwrap();
    f.seek(SeekFrom::Start(0)).unwrap();

    let mut data = unsafe {
        memmap::MmapOptions::new()
            .map_mut(&f)
            .expect("Could not access data from memory mapped file")
    };

    data[..src.len()].copy_from_slice(src.as_bytes());
}

请注意,此代码仍然有可能导致未定义的行为.由于切片由文件支持,因此文件的内容(以及切片)可能会在Rust程序之外的中更改,从而破坏了unsafe块应该保留的不变式.程序员需要确保文件在地图的生命周期内不会更改.不幸的是,板条箱本身并没有提供太多帮助来防止这种情况的发生,甚至没有任何文件警告用户.

Note that this is still possible for this code to lead to undefined behavior. Since the slice is backed by a file, the contents of the file (and thus the slice) may change from outside of the Rust program, breaking the invariants that the unsafe block is supposed to hold. The programmer needs to ensure that the file doesn't change during the lifetime of the map. Unfortunately, the crate itself does not provide much assistance to prevent this from happening or even any documentation warning the user.

如果您希望使用较低级别的系统调用,则缺少两个主要部分:

If you wish to use lower-level system calls, you are missing two main parts:

  1. mmap 不会自行分配任何空间,因此您需要设置一些空间在文件中.没有这个,我在macOS上运行时会显示Illegal instruction: 4.

  1. mmap doesn't allocate any space on its own, so you need to set some space in the file. Without this, I get Illegal instruction: 4 when running on macOS.

MemoryMap(是)默认情况下为私有,因此您需要将映射标记为公共,以便将更改写回到文件中(假设您要保存写入内容).没有这个,代码就会运行,但是文件永远不会改变.

MemoryMap (was) private by default so you need to mark the mapping as public so that changes are written back to the file (I'm assuming you want the writes to be saved). Without this, the code runs, but the file is never changed.

以下是适用于我的版本:

Here's a version that works for me:

use libc; // 0.2.67
use std::{
    fs::OpenOptions,
    io::{Seek, SeekFrom, Write},
    os::unix::prelude::AsRawFd,
    ptr,
};

fn main() {
    let src = "Hello!";

    let size = 1024 * 1024;

    let mut f = OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open("test.mmap")
        .expect("Unable to open file");

    // Allocate space in the file first
    f.seek(SeekFrom::Start(size as u64)).unwrap();
    f.write_all(&[0]).unwrap();
    f.seek(SeekFrom::Start(0)).unwrap();

    // This refers to the `File` but doesn't use lifetimes to indicate
    // that. This is very dangerous, and you need to be careful.
    unsafe {
        let data = libc::mmap(
            /* addr: */ ptr::null_mut(),
            /* len: */ size,
            /* prot: */ libc::PROT_READ | libc::PROT_WRITE,
            // Then make the mapping *public* so it is written back to the file
            /* flags: */ libc::MAP_SHARED,
            /* fd: */ f.as_raw_fd(),
            /* offset: */ 0,
        );

        if data == libc::MAP_FAILED {
            panic!("Could not access data from memory mapped file")
        }

        ptr::copy_nonoverlapping(src.as_ptr(), data as *mut u8, src.len());
    }
}

这篇关于如何创建和写入内存映射文件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆