use std::fs::File; use std::mem::ManuallyDrop; use std::os::unix::io::{FromRawFd, RawFd}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::{io, ptr};
impl MmapInner { /// Creates a new `MmapInner`. /// /// This is a thin wrapper around the `mmap` system call. fn new(
len: usize,
prot: libc::c_int,
flags: libc::c_int,
file: RawFd,
offset: u64,
) -> io::Result<MmapInner> { let alignment = offset % page_size() as u64; let aligned_offset = offset - alignment;
let (map_len, map_offset) = Self::adjust_mmap_params(len, alignment as usize)?;
unsafe { let ptr = mmap(
ptr::null_mut(),
map_len as libc::size_t,
prot,
flags,
file,
aligned_offset as off_t,
);
let map_len = len + alignment; let map_offset = alignment;
// `libc::mmap` does not support zero-size mappings. POSIX defines: // // https://pubs.opengroup.org/onlinepubs/9699919799/functions/mmap.html // > If `len` is zero, `mmap()` shall fail and no mapping shall be established. // // So if we would create such a mapping, crate a one-byte mapping instead: let map_len = map_len.max(1);
// Note that in that case `MmapInner::len` is still set to zero, // and `Mmap` will still dereferences to an empty slice. // // If this mapping is backed by an empty file, we create a mapping larger than the file. // This is unusual but well-defined. On the same man page, POSIX further defines: // // > The `mmap()` function can be used to map a region of memory that is larger // > than the current size of the object. // // (The object here is the file.) // // > Memory access within the mapping but beyond the current end of the underlying // > objects may result in SIGBUS signals being sent to the process. The reason for this // > is that the size of the object can be manipulated by other processes and can change // > at any moment. The implementation should tell the application that a memory reference // > is outside the object where this can be detected; otherwise, written data may be lost // > and read data may not reflect actual data in the object. // // Because `MmapInner::len` is not incremented, this increment of `aligned_len` // will not allow accesses past the end of the file and will not cause SIGBUS. // // (SIGBUS is still possible by mapping a non-empty file and then truncating it // to a shorter size, but that is unrelated to this handling of empty files.)
Ok((map_len, map_offset))
}
/// Get the current memory mapping as a `(ptr, map_len, offset)` tuple. /// /// Note that `map_len` is the length of the memory mapping itself and /// _not_ the one that would be passed to `from_raw_parts`. fn as_mmap_params(&self) -> (*mut libc::c_void, usize, usize) { let offset = self.ptr as usize % page_size(); let len = self.len + offset;
// There are two possible memory layouts we could have, depending on // the length and offset passed when constructing this instance: // // 1. The "normal" memory layout looks like this: // // |<------------------>|<---------------------->| // mmap ptr offset ptr public slice // // That is, we have // - The start of the page-aligned memory mapping returned by mmap, // followed by, // - Some number of bytes that are memory mapped but ignored since // they are before the byte offset requested by the user, followed // by, // - The actual memory mapped slice requested by the user. // // This maps cleanly to a (ptr, len, offset) tuple. // // 2. Then, we have the case where the user requested a zero-length // memory mapping. mmap(2) does not support zero-length mappings so // this crate works around that by actually making a mapping of // length one. This means that we have // - A length zero slice, followed by, // - A single memory mapped byte // // Note that this only happens if the offset within the page is also // zero. Otherwise, we have a memory map of offset bytes and not a // zero-length memory map. // // This doesn't fit cleanly into a (ptr, len, offset) tuple. Instead, // we fudge it slightly: a zero-length memory map turns into a // mapping of length one and can't be told apart outside of this // method without knowing the original length. if len == 0 {
(self.ptr, 1, 0)
} else {
(unsafe { self.ptr.offset(-(offset as isize)) }, len, offset)
}
}
/// Construct this `MmapInner` from its raw components /// /// # Safety /// /// - `ptr` must point to the start of memory mapping that can be freed /// using `munmap(2)` (i.e. returned by `mmap(2)` or `mremap(2)`) /// - The memory mapping at `ptr` must have a length of `len + offset`. /// - If `len + offset == 0` then the memory mapping must be of length 1. /// - `offset` must be less than the current page size. unsafefn from_raw_parts(ptr: *mut libc::c_void, len: usize, offset: usize) -> Self {
debug_assert_eq!(ptr as usize % page_size(), 0, "ptr not page-aligned");
debug_assert!(offset < page_size(), "offset larger than page size");
/// Open an anonymous memory map. pubfn map_anon(
len: usize,
stack: bool,
populate: bool,
huge: Option<u8>,
) -> io::Result<MmapInner> { let stack = if stack { MAP_STACK } else { 0 }; let populate = if populate { MAP_POPULATE } else { 0 }; let hugetlb = if huge.is_some() { MAP_HUGETLB } else { 0 }; let offset = huge
.map(|mask| ((mask as u64) & (MAP_HUGE_MASK as u64)) << MAP_HUGE_SHIFT)
.unwrap_or(0);
MmapInner::new(
len,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_PRIVATE | libc::MAP_ANON | stack | populate | hugetlb,
-1,
offset,
)
}
pubfn flush(&self, offset: usize, len: usize) -> io::Result<()> { let alignment = (self.ptr as usize + offset) % page_size(); let offset = offset as isize - alignment as isize; let len = len + alignment; let result = unsafe { libc::msync(self.ptr.offset(offset), len as libc::size_t, libc::MS_SYNC) }; if result == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
pubfn flush_async(&self, offset: usize, len: usize) -> io::Result<()> { let alignment = (self.ptr as usize + offset) % page_size(); let offset = offset as isize - alignment as isize; let len = len + alignment; let result = unsafe { libc::msync(self.ptr.offset(offset), len as libc::size_t, libc::MS_ASYNC) }; if result == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
fn mprotect(&mutself, prot: libc::c_int) -> io::Result<()> { unsafe { let alignment = self.ptr as usize % page_size(); let ptr = self.ptr.offset(-(alignment as isize)); let len = self.len + alignment; let len = len.max(1); if libc::mprotect(ptr, len, prot) == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
}
unsafe { let new_ptr = libc::mremap(old_ptr, old_len, map_len, options.into_flags());
if new_ptr == libc::MAP_FAILED {
Err(io::Error::last_os_error())
} else { // We explicitly don't drop self since the pointer within is no longer valid.
ptr::write(self, Self::from_raw_parts(new_ptr, new_len, offset));
Ok(())
}
}
}
impl Drop for MmapInner { fn drop(&mutself) { let (ptr, len, _) = self.as_mmap_params();
// Any errors during unmapping/closing are ignored as the only way // to report them would be through panicking which is highly discouraged // in Drop impls, c.f. https://github.com/rust-lang/lang-team/issues/97 unsafe { libc::munmap(ptr, len as libc::size_t) };
}
}
unsafeimpl Sync for MmapInner {} unsafeimpl Send for MmapInner {}
match PAGE_SIZE.load(Ordering::Relaxed) { 0 => { let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize };
PAGE_SIZE.store(page_size, Ordering::Relaxed);
page_size
}
page_size => page_size,
}
}
pubfn file_len(file: RawFd) -> io::Result<u64> { // SAFETY: We must not close the passed-in fd by dropping the File we create, // we ensure this by immediately wrapping it in a ManuallyDrop. unsafe { let file = ManuallyDrop::new(File::from_raw_fd(file));
Ok(file.metadata()?.len())
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-22)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.