Ответ 1
Реальный ответ состоит в том, чтобы использовать ящик, который обеспечивает эту функциональность, в идеале кроссплатформенным способом.
extern crate memmap;
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
блок. Программист должен убедиться, что файл не изменяется в течение жизни карты. К сожалению, сама корзина не оказывает большой помощи, чтобы предотвратить это, или даже любую документацию, предупреждающую пользователя.
Если вы хотите использовать системные вызовы более низкого уровня, вам не хватает двух основных частей:
-
mmap
не выделяет места самостоятельно, поэтому вам нужно установить некоторое место в файле. Без этого я получаюIllegal instruction: 4
при работе на macOS. -
MemoryMap
(был)MemoryMap
по умолчанию, поэтому вам нужно пометить отображение как общедоступное, чтобы изменения записывались обратно в файл (я предполагаю, что вы хотите, чтобы записи были сохранены). Без этого код выполняется, но файл никогда не изменяется.
Вот версия, которая работает для меня:
extern crate libc;
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,
) as *mut u8;
if data.is_null() {
panic!("Could not access data from memory mapped file")
}
ptr::copy_nonoverlapping(src.as_ptr(), data, src.len());
}
}