Rust能力养成系列之(25): 内存管理与安全

前言

对于使用低级编程语言(low-level programming language)的人来说,内存管理是需要理解到位的基本概念。低级语言没有内置垃圾收集器(built-in garbage collector)之类的自动内存回收解决方案,不得已由程序员来管理程序所使用的内存。自然,了解程序中内存的使用位置和方式,可以使程序员构建高效和安全的软件系统。根据各种bug的调查报告,许多低级语言所写的软件bug是由于内存处理不当造成的。有时,这的确是程序员的错误;而另一些时候,则是其所使用的编程语言的不完善所触发,如C和C++,这两种语言因软件中大量的内存漏洞报告而令人感情复杂。相比之下,Rust为内存管理提供了更好的编译时(compile-time)解决方案。这就大大改善了内存泄漏的情况,除非明确打算这样做!而且,使用Rust进行过大量开发的程序员最终会认识到,它会阻止糟糕的编程实践,并指导程序员编写安全有效的使用内存的软件。

Rust能力养成系列之(25): 内存管理与安全_第1张图片

在这一章中,我们将深入了解Rust是如何控制程序中资源所使用的内存的,还会简要介绍进程、内存分配、内存管理以及内存安全的含义。然后,我们将浏览Rust提供的内存安全模型,并理解使其能够在编译时跟踪内存使用情况的概念。读者将看到如何使用trait来控制类型在内存中的驻留位置以及它们何时被释放。最后,还将深入研究各种智能指针(smart pointer)类型,其提供了管理程序中的资源的抽象。

 

本章涉及的主题如下:

  • 程序和内存(Programs and memory)
  • 内存分配和安全(Memory allocations and safety)
  • 内存管理(Memory management)
  • 栈和堆(Stack and Heap)
  • 安全所有权、借用和生命期(Trifecta of safety—Ownership, borrowing, and lifetimes)
  • 智能指针类型(Smart pointer types)

 

程序和内存(Programs and memory)

Rust能力养成系列之(25): 内存管理与安全_第2张图片

程序是如何由操作系统运行的?有是什么机制允许程序使用内存来满足其需求?对开发人员而言,于此有一个大致的了解是很重要的,而这可以作为理解内存及其管理机制的充分理由。

众所周知,每个程序都需要内存才能运行,无论是某些程序员最喜欢的命令行工具还是复杂的流处理服务(command-line tool or a complex stream processing service),而它们对内存的需求截然不同。在主流的操作系统实现中,一个个正在执行的程序被实现为一个个进程(process),可见进程则是程序正在运行的实例。当我们在Linux的shell中执行./my_program或在Windows上双击my_program.exe时,操作系统将my_program作为一个进程加载到内存中,并开始执行,同时与其他进程一起,共享CPU和内存。再者,操作系统给进程分配对应的虚拟地址空间,保证这个虚拟地址空间不同于其他进程的虚拟地址空间,并且有建立相应的内存视图(memory view)。

在进程的生命周期内,会使用许多系统资源。首先,它需要内存来存储自己的指令,然后需要一定空间来存储在运行时执行指令期间所需的资源,接着启动一种方法来跟踪函数调用、任何局部变量和最后调用函数后返回的地址;自然所有这些都是需要内容空间的。其中一些内存需求可以在编译时提前决定,比如将基本类型存储在变量中;而其他需求则只能在运行时满足,比如创建一个动态数据类型,比如Vec。由于内存需求的不同层次,以及出于安全目的方面的考虑,进程的内存视图被划分为称为各种内存布局(memory layout)的区域。

Rust能力养成系列之(25): 内存管理与安全_第3张图片

这种布局根据存储的数据类型和提供的功能被划分为不同的区域。我们所关注的主要部分如下:

  • 文本段(Text segment):这个部分包含要在编译后的二进制文件中执行的实际代码。文本段是只读段,禁止任何用户修改代码,如果这样做的话,可能会导致程序崩溃。
  • 数据段(Data segment):数据段可以被进一步划分为子段,即初始化的数据段和未初始化的数据段( initialized data segment and uninitialized data segment,)曾被称为由符号开始的块(Block Started by Symbol ,BSS),用于保存程序中声明的所有全局值和静态值;未初始化的值在加载到内存时被初始化为0。
  • 栈段(Stack segment):这个段用来保存任何局部变量和函数的返回地址。所有预先知道大小的资源和程序创建的任何临时/中间变量都隐式存储在栈中。
  • 堆段(Heap segment):这个段用于存储任何动态分配的数据,这些数据的大小事先不知道,并且可以在运行时根据程序的需要更改。当我们期待相关值在函数中比它们的声明存在时间长时,这是一个理想的分配位置。

 

结语

本篇内容是后续内容的必要基础,详细的内容可以参见那本著名的宝书,《深入理解计算机系统》,说到这里,想到要是这本书出个Rust版是不是会更好呢。下一篇起,我们看下内存分配。

 

主要参考和建议读者进一步阅读的文献

https://doc.rust-lang.org/book

Rust编程之道,2019, 张汉东

The Complete Rust Programming Reference Guide,2019, Rahul Sharma,Vesa Kaihlavirta,Claus Matzinger

Hands-On Data Structures and Algorithms with Rust,2018,Claus Matzinger

Beginning Rust ,2018,Carlo Milanesi

Rust Cookbook,2017,Vigneshwer Dhinakaran

 

 

 

 

你可能感兴趣的:(Rust能力养成系列之(25): 内存管理与安全)