Rust-内存安全

这篇具有很好参考价值的文章主要介绍了Rust-内存安全。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

堆和栈

一个进程在执行的时候,它所占用的内存的虚拟地址空间一般被分割成好几个区域,我们称为“段”(Segment)。常见的几个段如下。

  • 代码段。编译后的机器码存在的区域。一般这个段是只读的。
  • bss段。存放未初始化的全局变量和静态变量的区域。
  • 数据段。存放有初始化的全局变量和静态变量的区域。
  • 函数调用栈(call stack segment)。存放函数参数、局部变量以及其他函数调用相关信息的区域。
  • 堆(heap)。存放动态分配内存的区域。

函数调用栈(call stack)也可以简称为栈(stack)。

因为函数调用栈本来就是基于栈这样一个数据结构实现的。

它具备“后入先出”(LIFO)的特点。最先进入的数据也是最后出来的数据。

一般来说,CPU有专门的指令可以用于入栈或者出栈的操作。

当一个函数被调用时,就会有指令把当前指令的地址压入栈内保存起来,然后跳转到被调用的函数中执行。

函数返回的时候,就会把栈里面先前的指令地址弹出来继续执行,如图所示。
Rust-内存安全,Rust,rust,安全,java

堆是为动态分配预留的内存空间,如图所示。
Rust-内存安全,Rust,rust,安全,java

和栈不一样,从堆上分配和重新分配块没有固定模式,用户可以在任何时候分配和释放它。这样就使得跟踪哪部分堆已经被分配和被释放变得异常复杂;有许多定制的堆分配策略用来为不同的使用模式下调整堆的性能。堆是在内存中动态分配的内存,是无序的。每个线程都有一个栈,但是每一个应用程序通常都只有一个堆。在堆上的变量必须要手动释放,不存在作用域的问题。

段错误

segfault实际上是“segmentation fault”的缩写形式,我们可以翻译为“段错误”。

segfault是这样形成的:进程空间中的每个段通过硬件MMU映射到真正的物理空间;

在这个映射过程中,我们还可以给不同的段设置不同的访问权限,比如代码段就是只能读不能写;

进程在执行过程中,如果违反了这些权限,CPU会直接产生一个硬件异常;

硬件异常会被操作系统内核处理,一般内核会向对应的进程发送一条信号;

如果没有实现自己特殊的信号处理函数,默认情况下,这个进程会直接非正常退出;

如果操作系统打开了core dump功能,在进程退出的时候操作系统会把它当时的内存状态、寄存器状态以及各种相关信息保存到一个文件中,供用户以后调试使用。

在传统系统级编程语言C/C++里面,制造segfault是很容易的。程序员需要非常小心才能避免这种错误,这也是为什么会有那么多的代码标准来规范程序员的行为。

而另外一类编程语言规避segfault的办法是使用自动垃圾回收机制。

在这些编程语言中,指针的能力被大幅限制,内存分配和释放都在一个运行时环境中被严格管理。

当然,这么做也付出了一定的代价。某些应用场景下用这样的代价换取开发效率和安全性是非常划算的,而在某些应用场景下这样的代价是不可接受的。

Rust的主要设计目标之一,是在不用自动垃圾回收机制的前提下避免产生segfault。从这个意义上来说,它是独一无二的。

内存安全

在谈到Rust的时候,经常会提到的一个概念,那就是“内存安全”(Memory safety)。

内存安全是Rust设计的主要目标之一,因此我们有必要把这个概念做一个澄清,让大家能更清楚地理解Rust为什么要这么设计。

  • 空指针
    解引用空指针是不安全的。这块地址空间一般是受保护的,对空指针解引用在大部分平台上会产生segfault。

  • 野指针
    野指针指的是未初始化的指针。它的值取决于它这个位置以前遗留下来的是什么值。所以它可能指向任意一个地方。对它解引用,可能会造成segfault,也可能不会,纯粹凭运气。但无论如何,这个行为都不会是你预期内的行为,是一定会产生bug的。

  • 悬空指针
    悬空指针指的是内存空间在被释放了之后,继续使用。它跟野指针类似,同样会读写已经不属于这个指针的内容。

  • 使用未初始化内存
    不只是指针类型,任何一种类型不初始化就直接使用都是危险的,造成的后果我们完全无法预测。

  • 非法释放
    内存分配和释放要配对。如果对同一个指针释放两次,会制造出内存错误。如果指针并不是内存分配器返回的值,对其执行释放操作,也是危险的。

  • 缓冲区溢出
    指针访问越界了,结果也是类似于野指针,会读取或者修改临近内存空间的值,造成危险。

  • 执行非法函数指针
    如果一个函数指针不是准确地指向一个函数地址,那么调用这个函数指针会导致一段随机数据被当成指令来执行,是非常危险的。

  • 数据竞争
    在有并发的场景下,针对同一块内存同时读写,且没有同步措施。

以上这些问题都是极度危险的,而且它们并不一定会在发生的时候就被发现并立即终止。

它们不一定会直接触发core dump,有可能程序一直带病运行,只是结果一直有bug但却无法找到原因,因为真正的原因与表现之间没有任何肉眼可见的关联关系。

它们有可能造成非常随机的、难以复现和难以调试的诡异bug,就像武林高手一样神出鬼没,行踪不定。

它们也可能在经过许多步骤之后最终触发core dump,可惜此时早已不是案发第一现场,修复这种bug的难度极高。

在Rust语境中,还有一些内存错误是不算在“内存安全”范畴内的,比如内存泄漏以及内存耗尽。

内存泄漏显然是一种bug,但是它不会直接造成非常严重的后果,至少比上面列出的那些错误危险性要低一些,解决的办法也是完全不一样的。

同样,内存耗尽也不是事关安全性的问题,出现内存耗尽的时候,Rust程序的行为依然是确定性的和可控的(目前版本下,如果内存耗尽则发生panic,也有人认为在这种情况发生的时候,应该给个机会由用户自己处理,这种情况后面应该会有改进)。

另外,panic也不属于内存安全相关的问题。

panic和core dump之间有重要区别。panic是发生不可恢复错误后,程序主动执行的一种错误处理机制;而core dump则是程序失控之后,触发了操作系统的保护机制而被动退出的。

发生panic的时候,此处就是确定性的第一现场,我们可以根据call stack信息很快找到事发地点,然后修复。panic是防止更严重内存安全错误的重要机制。文章来源地址https://www.toymoban.com/news/detail-806577.html

到了这里,关于Rust-内存安全的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 用了这么多年Rust终于搞明白了内存分布!

    用了这么多年Rust终于搞明白了内存分布!

    Rust作为一门学习曲线十分陡峭的语言,掌握其核心基础数据结构的内存分布对学习Rust会有很大的帮助,即使对于已经熟悉Rust的同学,深入数据结构分布也能帮助到调优Rust程序。 接下来,我会由浅入深仔细介绍Rust的各个数据结构在内存中的分布情况,帮助大家学习Rust。 先

    2024年02月02日
    浏览(25)
  • Rust入门(十四):不安全Rust

    Rust 可以不强制执行内存安全保证,这被称为 不安全 Rust ( unsafe Rust ),这类代码会提供额外的超能力。 可以通过 unsafe 来切换到不安全 Rust,接着可以开启一个新的存放不安全代码的块,有五类可以在不安全 Rust 中进行而不能用于安全 Rust 的操作: 解引用裸指针 调

    2024年02月11日
    浏览(12)
  • 【Rust 基础篇】Rust 模式:高效、安全和灵活的匹配工具

    在编程中,经常需要对数据进行匹配和处理,例如从一个复杂的数据结构中提取特定的值,或者根据不同的情况执行不同的逻辑。Rust是一门现代的系统编程语言,它引入了一种称为\\\"模式\\\"(Pattern)的强大特性,使得数据的匹配和处理变得高效、安全和灵活。本篇博客将深入探

    2024年02月08日
    浏览(14)
  • Rust安全编码实践 Secure Coding Practices in Rust

    作者:禅与计算机程序设计艺术 Rust编程语言被称为可保证内存安全的系统编程语言,它在编译期间通过类型系统确保数据不出错。因此,Rust语言开发者需要掌握一些安全编码实践,如内存安全、访问控制、输入验证等。本文将对这些安全编码实践进行详细介绍,并结合Rus

    2024年02月04日
    浏览(9)
  • Rust安全之数值

    编译通过,运行失败 cargo run 1 编译不通过 输出

    2024年02月11日
    浏览(4)
  • rust学习-不安全操作

    在 Rust 中,不安全代码块用于避开编译器的保护策略 解引用裸指针 通过 FFI (Foreign Function Interface,外部语言函数接口)调用函数 调用不安全的函数 内联汇编(inline assembly) 原始指针(raw pointer,裸指针)* 和引用 T 有类似的功能 引用总是安全的 ,因为借用检查器保证了它

    2024年02月11日
    浏览(4)
  • 几个开源 RUST 安全算法库

    几个开源 RUST 安全算法库

    这段时间把 RUST 语法过了一遍,写一些简单的 Demo 程序没啥问题了,但离掌握这门语言还差的远,需要项目实战才行。我决定从之前研究过的国密算法入手,使用 RUST 实现国密算法。 从头编写算法不太现实,上网搜了一下,还好已经有一些 开源 RUST 安全算法库,基于现有的

    2024年02月04日
    浏览(10)
  • TrustMe用Rust实现安全可信计算

    作者:禅与计算机程序设计艺术 Trusted Computing(简称TC)是一个现代信息系统工程的重要分支,其目的是通过可信任的计算环境构建具有高度安全性的安全计算解决方案。其定义为“一种建立在可信任基础上的系统,其处理的数据、计算资源、应用程序等在被授权时能提供某

    2024年02月10日
    浏览(10)
  • Rust字符串:安全、高效和灵活的数据类型

    Rust字符串:安全、高效和灵活的数据类型

    Rust是一种现代的系统级编程语言,以其出色的内存安全性和高性能而受到广泛关注。在Rust中,字符串是一种重要的数据类型,它具有独特的特点,使其在处理文本和字符数据时成为理想的选择。本文将深入探讨Rust字符串的特性,包括安全性、高效性和灵活性,以帮助您更好

    2024年01月19日
    浏览(14)
  • Rust 实现线程安全的 Lock Free 计数器

    完整代码:https://github.com/chiehw/hello_rust/blob/main/crates/counter/src/lib.rs Trait 可以看作是一种 能力的抽象 ,和接口有点类似。Trait 还能作为 泛型约束条件 ,作为参数的限制条件。 使用测试驱动开发可以让目标更明确,这里先写个简单的测试案例。 直接封装 AtomicUsize 使用多线程

    2024年04月13日
    浏览(8)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包