1 个不稳定版本
0.1.0 | 2023年3月24日 |
---|
#296 in 内存管理
73KB
1.5K SLoC
memory_pages:低级内存管理的高级API
虽然在一个项目中使用低级内存管理可以带来实质性的好处,但它通常是繁琐的。不同操作系统之间的API差异很大,并且存在许多容易陷入的陷阱。但如果,所有的不安全性和平台特定的差异都可以简单地抽象掉呢?如果可以做到非常细粒度的内存调整,而无需看到指针呢?这个crate提供了这样的零成本抽象。
它是为谁准备的?
这个crate主要面向性能关键的项目,特别是处理大量数据的项目。这个crate提供的API和类型不是所有问题的通用解决方案。因为这个crate是为这些应用程序设计的,所以它可以在使用标准内存管理的基础上实现巨大的改进(分配速度快2倍)。这个crate的另一个优点是 PagedVec
的 clear_decommit
函数,它不仅清除了向量,还通知操作系统正在清除向量。这个信息允许存储数据的物理内存页面被解引用,使得 PagedVec
在写入之前不占用RAM空间,同时仍然保留所需的空间。
memory_pages
提供了什么?
通过严格类型保证安全
使用低级API的常见陷阱之一是关于页面权限的错误。混淆它们至多会导致段错误,在最坏的情况下会引入严重的安全漏洞。这个crate利用Rust的类型系统(零大小标记类型)来确保某些类型的错误根本不可能发生。尝试获取一个可变引用,并将数据写入标记为只读的数据,通常会导致段错误并在运行时崩溃。一个称为 Pages
的内存页面集合,必须有一个 AllowWrite
标记类型,才能实现允许写入的功能和特质。这将所有类型的可怕运行时错误转换为编译时错误,使得不可能忽略它们。
指导但不限制。
本库的核心理念是始终引导,从不限制。几乎可以用这个库完成的所有操作,都可以在不看到单词 unsafe
的情况下完成。虽然引用和特殊的 FnRef
类型会在权限更改时自动失效,但可以通过使用 unsafe 函数和指针轻松绕过这些安全限制。有一些非常不安全的 API,它们被锁定在功能门后面。
高性能
直接获取内存页并削减中间人可以快2倍!
内存获取路径 | 分配50 MB结构体的空间速度 |
---|---|
标准分配器 | 6.5299 微秒 |
手动分配页面 | 3.5670 微秒 |
向内核提供有用的提示
使用如 adivse_use_soon
、advise_use_seq
、adivse_use_rng
等函数提供内存使用提示。
权力越大,责任越大
正如这个库可以大大提高性能并减少内存使用一样,它也可以降低性能并增加内存使用。要达到比使用默认分配器更差的结果相当困难,需要非常非常粗心,而从这个库中榨取一切则更难。使用的收益取决于用户的能力。作为一个好例子,在某些测试中,实现了2倍的分配时间减少。但这些例子被广泛调整,以找到最大的潜在性能提升。
内核内存使用提示
内存使用将是顺序的还是随机的?向内核提供可选的提示,这可能在某些情况下提高性能。
示例
直接处理页面
数据存储
use memory_pages::*;
let mut memory = Pages<AllowRead,AllowWrite,DenyExec> = Pages::new(0x40000);
read_data(&mut memory).unwrap();
validate_data(&mut memory);
防止写入
use memory_pages::*;
let mut memory = Pages<AllowRead,AllowWrite,DenyExec> = Pages::new(0x40000);
read_data(&mut memory).unwrap();
let mut memory.deny_write();
// `memory` is now read-only and a write attempt would case a segfault
// Because of that, borrowing it as `&mut [u8]` is now not avalible, so this would not compile if used
// write_data(&mut memory);
x86_64运行时汇编的函数
由于调用约定不同,此示例在Windows上不起作用
use memory_pages::*;
let mut memory:Pages<AllowRead,AllowWrite,DenyExec> = Pages::new(0x4000);
// hex-encoded X86_64 assembly for adding 2 numbers
// lea rax, [rdi+rsi]
memory[0] = 0x48;
memory[1] = 0x8d;
memory[2] = 0x04;
memory[3] = 0x37;
// ret
memory[4] = 0xC3;
// Sets execution to allow and write to denny to prevent exploits
let memory = memory.set_protected_exec();
//TODO: this should check for lifetimes!
let add:extern "C" fn(u64,u64)->u64 = unsafe{memory.get_fn(0)};
assert_eq!(add(43,34),77);
PagedVec
创建新的vec
let mut vec = PagedVec::new(0x10000);
for _ in 1_000_000{
vec.push(102.32);
}
清除和去引用
let mut vec = PagedVec::new(0x10000);
for _ in 1_000_000{
vec.push(102.32);
}
// Clears vec, keeping the capacity but freeing the phisical memory,
// which is automaticaly reclaimed as it is filled back up.
vec.clear_decommit();
依赖项
~175KB