当前位置: 首页 > 知识库问答 >
问题:

如何创建和写入内存映射文件?

吕昀
2023-03-14

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

我正在尝试使用std::os::MemoryMap创建内存映射文件。目前的做法如下:

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或对数据执行任何其他操作时。

  • 我无法从内存映射中写入(或读取)数据的原因是什么

共有2个答案

封鸿雪
2023-03-14

最新版本:

use std::ptr;
use std::fs;
use std::io::{Write, SeekFrom, Seek};
use std::os::unix::prelude::AsRawFd;
use mmap::{MemoryMap, MapOption};

// from crates.io
extern crate mmap;
extern crate libc;

fn main() {
    let size: usize = 1024*1024;

    let mut f = fs::OpenOptions::new().read(true)
                                      .write(true)
                                      .create(true)
                                      .open("test.mmap")
                                      .unwrap();

    // 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();

    let mmap_opts = &[
        // Then make the mapping *public* so it is written back to the file
        MapOption::MapNonStandardFlags(libc::consts::os::posix88::MAP_SHARED),
        MapOption::MapReadable,
        MapOption::MapWritable,
        MapOption::MapFd(f.as_raw_fd()),
    ];

    let mmap = MemoryMap::new(size, mmap_opts).unwrap();

    let data = mmap.data();

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

    let src = "Hello!";
    let src_data = src.as_bytes();

    unsafe {
        ptr::copy(src_data.as_ptr(), data, src_data.len());
    }
}
席兴朝
2023-03-14

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

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程序外部更改,从而破坏不安全块应该持有的不变量。程序员需要确保文件在映射的生命周期内不会更改。不幸的是,板条箱本身没有提供太多的帮助来防止这种情况发生,甚至没有任何文档警告用户

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

>

  • mmap不会自行分配任何空间,因此需要在文件中设置一些空间。没有这个,我得到非法指令: 4当运行在macOS.

    MemoryMap(was)默认为私有,因此您需要将映射标记为公共,以便将更改写回文件(我假设您希望保存写入)。如果不这样做,代码将运行,但文件永远不会更改。

    以下是一个适合我的版本:

    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());
        }
    }
    

  •  类似资料:
    • 我想写一个字节向量,

    • 读 # mmap_read.py import mmap with open('lorem.txt', 'r') as f: with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as m: print('First 10 bytes via read :', m.read(10

    • 假设我有一组字符串和一个散列函数(或任何单边函数)和一个测试函数。我想用Java8流创建一个从输入字符串到通过测试函数的哈希值的映射。我的问题是如何在中编写? 看来老的for循环是最简洁的解决方案。

    • 我试图将数据写入Rust中的内存映射文件,但它不会映射指定的文件,因为它声明给定的fd不可用。 我可以在文件系统上看到它,因此它确实以正确的权限存在。我怀疑这是一个bug,或者我没有以正确的方式使用新的IO API。 这是密码

    • 问题内容: 最近,我碰到了这篇文章,这篇文章很好地介绍了内存映射文件以及如何在两个进程之间共享它。这是读取文件的过程的代码: 但是,我对这种方法有几点评论/问题: 如果我们仅对空文件执行读取器,即运行 这将分配8000个字节,现在将扩展文件。返回的缓冲区的限制为8000,位置为0,因此,读取器可以继续读取空数据。发生这种情况后,阅读器将停止,如。 现在应该是作家了(代码被省去了,因为它很简单,可以

    • 系统调用在调用进程的虚拟地址空间中提供映射,将文件或设备映射到内存中。 下面是两种类型 - 文件映射或文件支持的映射 - 此映射将进程的虚拟内存区域映射到文件。 这意味着读取或写入这些内存区域会导致文件被读取或写入。这是默认的映射类型。 匿名映射 - 此映射映射进程的虚拟内存区域,不受任何文件的支持。 内容被初始化为零。 这种映射类似于动态内存分配(malloc()),在某些实现中用于某些分配。