Segmentation Fault Core Dumped Ubuntu

2025年3月17日 | 阅读 8 分钟

什么是段错误?

段错误Segmentation Fault)或访问冲突(简称为segfault)是一种硬件内存保护产生的故障,它指示操作系统软件尝试访问一个受限制的内存区域(内存访问冲突)。

在标准的 x86 系统上,这是一种通用保护性故障。作为响应,操作系统内核通常会执行一些纠正操作,一般是通过向进程发送一个信号来将该故障转交给有问题的进程。

在某些情况下,进程可以安装一个信号处理器,允许它们自行恢复,否则将使用操作系统的默认信号处理器,通常会导致进程异常终止,有时还会产生核心转储(core dump)。

  • 段错误是许多语言(例如提供低级内存访问和一些安全检查的C语言)中程序的常见错误类型。
  • 主要原因通常是由于在使用指针进行虚拟内存寻址时出现多种错误,特别是非法访问。
  • 其他类型的内存访问错误包括总线错误,它也有多种原因。
  • 总线错误主要由于不准确的物理内存寻址或未对齐的内存访问引起。
  • 这些是硬件无法寻址的内存引用,而不是进程无权访问的引用。
  • 多种编程语言可能应用旨在避免段错误并实现内存安全的机制。例如,Rust编程语言应用一种基于所有权的模式来确保内存安全。而JavaLisp等语言则应用垃圾回收机制,它忽略了许多可能导致段错误的内存错误类型。

段错误概述

  • 当任何程序尝试访问它无权访问的内存位置,或以不允许的方式访问内存位置(例如,尝试写入只读位置,或覆盖操作系统部分)时,就会发生段错误。
  • 在计算领域,“分段”一词有多种用法。在段错误(自 20 世纪 50 年代开始使用)的语境下,它定义了程序的地址空间,只有程序的地址空间可以通过内存保护进行读取,并且其中只有栈和数据段的可读写部分是可写的。因此,尝试读取程序地址空间之外的内容,或写入地址空间的只读部分会导致段错误。
  • 如果硬件检测到尝试引用一个不存在的段、一个超出段边界的位置,或在使用硬件内存分段提供虚拟内存的系统中,在未经允许的情况下访问某个段,就会发生段错误。
  • 通常,在仅使用分页的系统上,无效的页错误会导致段错误。页错误和段错误都是由虚拟内存管理系统引起的故障。此外,段错误可以独立于页错误发生;对任何有效页的非法访问就是段错误。段错误可能出现在页的中间。例如,发生在页内但非法覆盖内存的缓冲区溢出
  • 最初,该故障是由MMU内存管理单元)在非法访问时产生的,作为其内存保护方面的一部分,或在硬件层面产生无效页错误。如果问题不是无效的逻辑地址而是无效的物理地址,则会发生总线错误。因此,这些不总是区分开来的。
  • 发现此故障后,会向有问题的进程发送信号,从而在操作系统层面激活进程的信号处理器。不同类型的操作系统有不同的信号名称来指示发生了段错误。
  • 在类 Unix 操作系统上,一个称为 SIGSEGVsegmentation violation 的缩写)的信号会被发送给有问题的进程。在 Microsoft Windows 上,有问题的进程会收到一个异常,即 STATUS_ACCESS_VIOLATION

段错误的原因

发生段错误的情况及其表现方式因操作系统和硬件而异。不同的硬件会为不同的情况产生不同的故障,不同的操作系统会将这些故障转换为发送给进程的不同信号。

根本原因可能是软件的某种错误,导致了内存访问冲突。在某些情况下,程序会一致地导致段错误,从而使调试或确定根本原因变得容易。然而,在其他情况下,bug 可能难以重现,并且依赖于每次运行的内存分配。

以下是一些典型的段错误原因:

  • 尝试访问不存在的内存地址(超出进程地址空间范围)
  • 尝试访问程序没有权限访问的内存(例如,在进程上下文中访问内核结构
  • 尝试写入只读内存(例如,代码段
  • 这些通常是由多种编程错误引起的,这些错误会导致无效的内存访问:
  • 尝试执行一个没有正确编译的程序。(一些编译器即使存在编译时错误也会生成可执行文件。)
  • 栈溢出
  • 缓冲区溢出
  • 对已释放指针进行赋值或解引用(悬垂指针,指向已被删除/去分配/释放的内存)
  • 未初始化指针进行赋值或解引用(野指针,指向随机内存地址)
  • 解引用空指针通常表示一个地址,该地址不是进程地址空间的一部分

段错误经常是由于指针使用错误引起的,尤其是在 C 代码的C 动态内存分配中。解引用空指针会导致未定义的行为,并导致段错误。这是因为空指针不可能是有效的内存地址。悬垂指针野指针指向可能存在也可能不存在,可能可写也可能可读的内存,因此可能导致瞬时性 bug。

解决 Ubuntu 中的段错误

此错误可能在任何时候出现在我们的 Ubuntu 系统上。段错误是指我们的系统尝试访问不存在的任何内存页。Core dumped(核心转储)表示代码部分尝试对空闲或只读位置执行写入和读取操作。通常,segfaults 与名为 core 的文件相关联,并在升级过程中发生。

在核心转储情况下执行某些命令时,我们可能会遇到“无法打开锁文件”错误。这是因为系统正尝试获取一个不存在的块。这是由于某些特定程序的二进制文件崩溃造成的。

我们可能正在调试或回溯以解决此问题,但解决方案是通过执行以下提及的步骤来修复损坏的软件包:

1. 删除位于不同位置的锁文件。


Segmentation Fault Core Dumped Ubuntu

2. 删除仓库缓存。


Segmentation Fault Core Dumped Ubuntu

3. 升级和更新我们的仓库缓存。


Segmentation Fault Core Dumped Ubuntu

Segmentation Fault Core Dumped Ubuntu

4. 现在升级我们的发行版,它将更新我们的软件包。


Segmentation Fault Core Dumped Ubuntu

5. 搜索损坏的软件包并强制删除它们。

除了命令行之外,以下是一种始终有效的好方法:

  1. 在启动模式下,重新启动 Ubuntu 时,按 Esc 键。
  2. 选择“Ubuntu 的高级选项”
    Segmentation Fault Core Dumped Ubuntu
  3. 在恢复模式下运行 Ubuntu,会显示多个选项。
  4. 首先,选择“修复损坏的软件包”
    Segmentation Fault Core Dumped Ubuntu
  5. 然后,选择“继续正常启动”
    Segmentation Fault Core Dumped Ubuntu

现在,我们有两种方法可以解决段错误:GUI 和 CLI。有时,由于segfault,apt 命令可能无法正常工作,因此 CLI 方法将不起作用。在这种情况下,不用担心,因为 GUI 方法将始终对我们有效。

处理段错误

总线错误或段错误的默认行为是异常终止遇到它的进程。可能会生成核心文件以帮助调试,也可能执行其他平台相关的任务。例如,许多应用了grsecurity 补丁的 Linux 系统可能会记录 SIGSEGV 信号,以监视可能通过缓冲区溢出进行的入侵尝试。

在某些系统(如 Windows 和 Linux)上,程序本身可以处理段错误。运行的程序不仅可以管理事件,还可以提取其状态的一些详细信息,例如处理器寄存器值、堆栈跟踪、遇到错误的源代码行、被非法访问的内存地址,以及根据操作系统和架构,该任务是写入还是读取。

然而,虽然段错误表明程序存在需要修复的错误,但也可以有意地引起这种故障,用于测试目的、调试,以及模仿需要直接内存访问的平台。在后一种情况下,系统应能够允许程序在错误发生后继续执行。

在这种情况下,当系统允许时,可以管理事件并增强处理器程序计数器以“跳转”到失败的指令继续执行。

段错误的示例

Segmentation Fault Core Dumped Ubuntu

写入只读内存

这会引起段错误。当程序因代码错误在代码段或数据段的只读部分写入时,就会发生这种情况,因为这些部分由操作系统加载到只读内存中。

空指针解引用

在 C 和其他类 C 语言中,空指针用于表示“指向空对象的指针”,并作为错误指示符。对空指针进行解引用(对空指针进行读写)是一个非常基本的程序错误。

标准并没有规定空指针等同于指向内存地址 0 的指针,尽管这可能是实际情况。几乎所有的操作系统都会映射空指针地址,使其使用该指针会导致段错误。

C 标准不保证此行为。在 C 语言中,解引用空指针是未定义行为,符合标准的实现可以假定被解引用的指针不是空指针。

缓冲区溢出

栈溢出


下一个主题Spotify Ubuntu