深入理解Rust中的内存管理:栈、堆与静态内存详解引言:内存管理是Rust编程语言的核心优势之一,它通过严格的借用检查和所有权规则确保程序的安全性和高效性。本文将深入探讨Rust中的三种主要内存区域——栈、堆和静态内存,帮助你理解它们的不同用途及在Rust中的具体表现。通过实际例子,我们将揭示这
引言:
内存管理是Rust编程语言的核心优势之一,它通过严格的借用检查和所有权规则确保程序的安全性和高效性。本文将深入探讨Rust中的三种主要内存区域——栈、堆和静态内存,帮助你理解它们的不同用途及在Rust中的具体表现。通过实际例子,我们将揭示这些内存区域如何影响变量的生命周期、指针的使用以及程序的性能。无论是新手还是有经验的开发者,都可以从中获取宝贵的知识。
例子
fn main() {
let x = 42;
let y = 43;
let var1 = &x;
let mut var2 = &x;
var2 = &y;
let s = "Hello World"; // 指针 执行变量第一个字符的位置
}
fn main() {
let a = String::from("123");
let b = a;
println!("{}", b);
// println!("{}", a);
}
fn main() {
let mut a = 123;
let b = &a;
let c = &mut a; // 报错
println!("{}", b);
println!("{}", c);
}
let dave = 123;
dave = 456;
fn main() {
let pw = "justok";
let is_strong = is_strong(pw);
}
// &str -> Stack; String -> Heap
//fn is_strong(password: String) -> bool {
// password.len() > 5
//}
fn is_strong<T: AsRef<str>>(password: T) -> bool {
password.as_ref().len() > 5
}
fn is_strong<T: Into<String>>(password: T) -> bool {
password.into().len() > 5
}
Vec<T>
fn main() {
let a: i32 = 40; // stack
let b: Box<i32> = Box::new(60); // Heap
//let result = a + b; // 报错
let result = a + *b;
println!("{} + {} = {}", a, b, a + *b);
}
Box<T>
) 就是指向 heap 上该值的指针。当 box 被丢弃时,内存就被释放use std::mem::drop;
fn main() {
let a = Box::new(1);
let b = Box::new(1);
let c = Box::new(1);
let result1 = *a + *b + *c;
drop(a);
let d = Box::new(1);
let result2 = *b + *c + *d;
println!("{} {}", result1, result2);
}
const X: i32 = 123;
Stack | Heap | 备注 |
---|---|---|
简单 | 复杂 | |
安全 | 危险 | 指 Unsafe Rust |
快 | 慢 | |
死板 | 灵活 |
use graphics::math::{add, mul_scalar, Vec2d};
use piston_window::*;
use rand::prelude::*;
use std::alloc::{GlobalAlloc, Layout, System};
use std::time::Instant;
use std::cell::Cell;
#[global_allocator]
static ALLOCATOR: ReportingAllocator = ReportingAllocator;
struct ReportingAllocator;
// Execute a closure without logging on allocations.
pub fn run_guarded<F>(f: F)
where
F: FnOnce(),
{
thread_local! {
static GUARD: Cell<bool> = Cell::new(false);
}
GUARD.with(|guard| {
if !guard.replace(true) {
f();
guard.set(false)
}
})
}
unsafe impl GlobalAlloc for ReportingAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let start = Instant::now();
let ptr = System.alloc(layout);
let end = Instant::now();
let time_taken = end - start;
let bytes_requested = layout.size();
// eprintln!("{}\t{}", bytes_requested, time_taken.as_nanos());
run_guarded(|| {eprintln!("{}\t{}", bytes_requested, time_taken.as_nanos())});
ptr
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
System.dealloc(ptr, layout);
}
}
struct World {
current_turn: u64,
particles: Vec<Box<Particle>>,
height: f64,
width: f64,
rng: ThreadRng,
}
struct Particle {
height: f64,
width: f64,
position: Vec2d<f64>,
velocity: Vec2d<f64>,
acceleration: Vec2d<f64>,
color: [f32; 4],
}
impl Particle {
fn new(world: &World) -> Particle {
let mut rng = thread_rng();
let x = rng.gen_range(0.0..=world.width);
let y = world.height;
let x_velocity = 0.0;
let y_velocity = rng.gen_range(-2.0..0.0);
let x_acceleration = 0.0;
let y_acceleration = rng.gen_range(0.0..0.15);
Particle {
height: 4.0,
width: 4.0,
position: [x, y].into(),
velocity: [x_velocity, y_velocity].into(),
acceleration: [x_acceleration, y_acceleration].into(),
color: [1.0, 1.0, 1.0, 0.99],
}
}
fn update(&mut self) {
self.velocity = add(self.velocity, self.acceleration);
self.position = add(self.position, self.velocity);
self.acceleration = mul_scalar(self.acceleration, 0.7);
self.color[3] *= 0.995;
}
}
impl World {
fn new(width: f64, height: f64) -> World {
World {
current_turn: 0,
particles: Vec::<Box<Particle>>::new(),
height: height,
width: width,
rng: thread_rng(),
}
}
fn add_shapes(&mut self, n: i32) {
for _ in 0..n.abs() {
let particle = Particle::new(&self);
let boxed_particle = Box::new(particle);
self.particles.push(boxed_particle);
}
}
fn remove_shapes(&mut self, n: i32) {
for _ in 0..n.abs() {
let mut to_delete = None;
let particle_iter = self.particles.iter().enumerate();
for (i, particle) in particle_iter {
if particle.color[3] < 0.02 {
to_delete = Some(i)
}
break;
}
if let Some(i) = to_delete {
self.particles.remove(i);
} else {
self.particles.remove(0);
};
}
}
fn update(&mut self) {
let n = self.rng.gen_range(-3..=3);
if n > 0 {
self.add_shapes(n);
} else {
self.remove_shapes(n);
}
self.particles.shrink_to_fit();
for shape in &mut self.particles {
shape.update();
}
self.current_turn += 1;
}
}
fn main() {
let (width, height) = (1280.0, 960.0);
let mut window: PistonWindow = WindowSettings::new("particles", [width, height])
.exit_on_esc(true)
.build()
.expect("Could not create a window.");
let mut world = World::new(width, height);
world.add_shapes(1000);
while let Some(event) = window.next() {
world.update();
window.draw_2d(&event, |ctx, renderer, _device| {
clear([0.15, 0.17, 0.17, 0.9], renderer);
for s in &mut world.particles {
let size = [s.position[0], s.position[1], s.width, s.height];
rectangle(s.color, size, ctx.transform, renderer);
}
});
}
}
Cargo.toml
[package]
name = "particles"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
piston2d-graphics = "0.43.0"
piston_window = "0.128.0"
rand = "0.8.5"
问题:运行Rust程序报错 51287 illegal hardware instruction cargo run
➜ cargo run
Finished dev 【unoptimized + debuginfo】 target(s) in 0.18s
Running `target/debug/particles`
【1】 29813 illegal hardware instruction cargo run
解决:
<https://github.com/rust-in-action/code/pull/106>
<https://github.com/rust-in-action/code/commit/a0731bc66504fdd74f4d548059cb6ad2fb34539a>
运行
cargo run
cargo run -q 2> alloc.tsv
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import font_manager
plt.rcParams["font.sans-serif"]=["Songti SC"]
df = pd.read_csv('/Users/qiaopengjun/rust/particles/alloc.tsv', sep='\t', header=None)
df.head()
df.columns
df.info
plt.figure(figsize=(25, 7))
plt.scatter(df[0], df[1], s=12, facecolors='none', edgecolors='b')
plt.xlim(0, 20000)
plt.ylim(0, 20000)
plt.xlabel('分配内存大小(byte)')
plt.ylabel('分配持续时间(ns)')
plt.show()
fn main() {
let mut n_nonzero = 0;
for i in 0..10000 { // 当 i = 0 None 指针
let ptr = i as *const u8;
let byte_at_addr = unsafe {*ptr};
if byte_at_addr != 0 {
n_nonzero += 1;
}
}
println!("内存中的非 0 字节:{}", n_nonzero);
}
修改之后
static GLOBAL: i32 = 1000;
fn noop() -> *const i32 {
let noop_local = 12345;
&noop_local as *const i32
}
fn main() {
let local_str = "a";
let local_int = 123;
let boxed_str = Box::new('b');
let boxed_int = Box::new(789);
let fn_int = noop();
println!("GLOBAL: {:p}", &GLOBAL as *const i32);
println!("local_str: {:p}", local_str as *const str);
println!("local_int: {:p}", &local_int as *const i32);
println!("boxed_int: {:p}", Box::into_raw(boxed_int));
println!("boxed_str: {:p}", Box::into_raw(boxed_str));
println!("fn_int: {:p}", fn_int);
}
目的:在程序运行的时候扫描程序的内存
main.rs 文件
use kernel32;
use winapi;
use winapi::{
DWORO, // Rust 里就是 u32
HANDLE, // 各种内部 API 的指针类型,没有关联类型。
// 在 Rust 里 std::os::raw::c_void 定义了 void 指针
LPVOID, // Handle 是指向 Windows 内一些不透明资源的指针
PVOID, // Windows 里,数据类型名的前缀通常是其类型的缩写
SIZE_T, // 这台机器上 u64 是 usize
LPSYSTEM_INFO, // 到 SYSTEM_INFO struct 的指针
MEMORY_BASIC_INFORMATION as MEMINFO, // Windows 内部定义的一些 Struct
SYSTEM_INFO,
};
fn main() {
// 这些变量将在 unsafe 块进行初始化
let this_pid: DWORO;
let this_proc: HANDLE;
let min_addr: LPVOID;
let max_addr: LPVOID;
let mut base_addr: PVOID;
let mut proc_info: SYSTEM_INFO;
let mut mem_info: MEMINFO;
const MEMINFO_SIZE: usize = std::mem::size_of::<MEMINFO>();
// 保证所有的内存都初始化了
unsafe {
base_addr = std::mem::zeroed();
proc_info = std::mem::zeroed();
mem_info = std::mem::zeroed();
}
// 系统调用
unsafe {
this_pid = kernel32::GetCurrentProcessId();
this_proc = kernel32::GetCurrentProcess();
// 下面代码使用 C 的方式将结果返回给调用者。
// 提供一个到预定义 Struct 的指针,一旦函数返回就读取 Struct 的新值
kernel32::GetSystemInfo(&mut proc_info as LPSYSTEM_INFO);
};
// 对变量重命名
min_addr = proc_info.lpMinimumApplicationAddress;
max_addr = proc_info.lpMaximumApplicationAddress;
println!("{:?} @ {:p}", this_pid, this_proc);
println!("{:?}", proc_info);
println!("min: {:p}, max: {:p}", min_addr, max_addr);
// 扫描地址空间
loop {
let rc: SIZE_T = unsafe {
// 提供运行程序内存地址空间特定段的信息,从 base_addr 开始
kernel32::VirtualQueryEx(this_proc, base_addr, &mut mem_info, MEMINFO_SIZE as usize)
};
if rc == 0 {
break;
}
println!("{:#?}", mem_info);
base_addr = ((base_addr as u64) + mem_info.RegionSize) as PVOID;
}
}
Cargo.toml 文件
[package]
name = "tlearn"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
winapi = "0.2" # 定义一些有用的类型别名,对所有 Windows API 的原始的 FFI 绑定
kernel32-sys = "0.2" # 提供与 KERNEL.DLL 的交互,包含 Windows API 库 Kernel32 的函数定义
let pid = some_process_id;
OpenProcess(pid);
loop 地址空间 {
调用 VirtualQueryEx() 来访问下个内存块
通过调用 ReadProcessMemory(),来扫描内存块
寻找某种特定的模式
使用所需的值调用 WriteProcessMemory()
}
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!