【问题标题】:Why am I getting segfaults randomly?为什么我会随机收到段错误?
【发布时间】:2009-05-21 17:46:32
【问题描述】:

这对我来说很奇怪,但是当我启动我的程序时,我遇到了一个意想不到的随机分段错误。有时它可以工作,有时它会崩溃.. Dev-C++ 的调试器将我指向文件的一行:stl_construct.h

/**
   * @if maint
   * Constructs an object in existing memory by invoking an allocated
   * object's constructor with an initializer.
   * @endif
   */
  template<typename _T1, typename _T2>
    inline void
    _Construct(_T1* __p, const _T2& __value)
    {
      // _GLIBCXX_RESOLVE_LIB_DEFECTS
      // 402. wrong new expression in [some_]allocator::construct
     -> ::new(static_cast<void*>(__p)) _T1(__value);
    }

顺便说一句,我正在广泛使用 STL。我应该怎么做才能检测到段错误的起源?有什么工具可以提供帮助吗?可能导致此类随机崩溃的原因是什么。

编辑:

我的程序大约有 5000 行代码。我不知道我必须显示哪段代码才能获得帮助,因为我不知道问题的根源,我从调试器得到的只是它与 STL 有关。

编辑:

我现在移到Code::Blocks,这里是调用堆栈:

#0 00464635 std::_Construct<std::pair<double const, int>, std::pair<double const, int> >(__p=0xb543e8, __value=@0x10) (C:/Program Files/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_construct.h:81)
#1 00462306 std::_Rb_tree<double, std::pair<double const, int>, std::_Select1st<std::pair<double const, int> >, std::less<double>, std::allocator<std::pair<double const, int> > >::_M_create_node(this=0x406fe50, __x=@0x10) (C:/Program Files/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_tree.h:367)
#2 00461DA7 std::_Rb_tree<double, std::pair<double const, int>, std::_Select1st<std::pair<double const, int> >, std::less<double>, std::allocator<std::pair<double const, int> > >::_M_clone_node(this=0x406fe50, __x=0x0) (C:/Program Files/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_tree.h:379)
#3 004625C6 std::_Rb_tree<double, std::pair<double const, int>, std::_Select1st<std::pair<double const, int> >, std::less<double>, std::allocator<std::pair<double const, int> > >::_M_copy(this=0x406fe50, __x=0x0, __p=0x406fe54) (C:/Program Files/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_tree.h:1029)
#4 00462A9D std::_Rb_tree<double, std::pair<double const, int>, std::_Select1st<std::pair<double const, int> >, std::less<double>, std::allocator<std::pair<double const, int> > >::_Rb_tree(this=0x406fe50, __x=@0xb59a7c) (C:/Program Files/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_tree.h:559)
#5 0045A928 std::map<double, int, std::less<double>, std::allocator<std::pair<double const, int> > >::map(this=0x406fe50, __x=@0xb59a7c) (C:/Program Files/CodeBlocks/MinGW/bin/../lib/gcc/mingw32/3.4.5/../../../../include/c++/3.4.5/bits/stl_map.h:166)
#6 0040B7E2 VehicleManager::get_vehicles_distances(this=0xb59a50) (C:/Program Files/CodeBlocks/MinGW/projects/AHS/VehicleManager.cpp:232)
#7 00407BDA Supervisor::IsMergeInstruction(id_vehicle=1) (C:/Program Files/CodeBlocks/MinGW/projects/AHS/Supervisor.cpp:77)
#8 00408430 CheckingInstructionsThread(arg=0x476100) (C:/Program Files/CodeBlocks/MinGW/projects/AHS/Supervisor.cpp:264)
#9 00413950 _glfwNewThread@4() (??:??)
#10 75A24911    KERNEL32!AcquireSRWLockExclusive() (C:\Windows\system32\kernel32.dll:??)
#11 00476100    std::__ioinit() (??:??)
#12 0406FFD4    ??() (??:??)
#13 76E5E4B6    ntdll!RtlInitializeNtUserPfn() (C:\Windows\system32\ntdll.dll:??)
#14 00476100    std::__ioinit() (??:??)
#15 70266582    ??() (??:??)
#16 00000000    ??() (??:??)

更多的精度:

1/ 这是一个多线程应用程序。 2/ 方法:get_vehicles_distances();返回地图。 3/ 可能地图在 IsMergeInstruction() 调用时还没有初始化;

编辑:

显然导致段错误的行是:

vehicles_distances_.erase(vehicles_distances_.begin(), vehicles_distances_.end());

vehicles_distances_ 是地图。这一行是方法的一部分:VehicleManager::MoveAllVehicles();

void VehicleManager::MoveAllVehicles() {

     vehicles_distances_.erase(vehicles_distances_.begin(), vehicles_distances_.end());

     vector<Vehicle>::iterator iter_end = VehicleManager::vehicles_.end();
     for(vector<Vehicle>::iterator iter = VehicleManager::vehicles_.begin();
     iter != iter_end; ++iter) {

          (*iter).MoveVehicle();

          vehicles_distances_[(*iter).get_vec_vehicle_position().y] = (*iter).get_id_vehicle();

     }

}

这有什么问题?

编辑:

我尝试使用 map::clear();作为 map::erase() 的替代品;但同样的问题出现了!

编辑:

我想我明白了...一个线程正在尝试使用车辆距离,而它已被清除..(?)

编辑:

问题解决了!所以它来自 map::erase();正如预期的那样。我通过创建另一个映射变量绕过了这个问题,其中 &lt;key, value&gt; 对被反转,所以我可以更新地图。 (因为我需要的关键是距离,并且距离不是唯一的,因为它每次都在变化,但 id_vehicle 是唯一的!)。最后我只是拿了那张地图,再次反转&lt;key, value&gt;并将其转移到可以在每个循环中重新声明的原始地图...

谢谢大家!

【问题讨论】:

  • 你至少可以给调用栈。
  • 它与 STL 无关。那行代码正是错误出现的地方。它是在程序的早期引起的,可能在任何地方。
  • 有没有可能将 vehicle_distances_ 声明为静态类? (请参阅下面关于静态初始化程序的不可预测顺序的回答。)

标签: c++ stl segmentation-fault


【解决方案1】:

首先,为了所有可爱的事物,不要使用 Dev-C++。我希望我知道人们是如何不断撞上那块垃圾的。它已经有 没有维护了,即使它得到维护,它仍然是一个缺乏基本功能的有缺陷的垃圾。放弃它,选择无数更好的免费替代品之一。

现在,回答您的问题:您的程序会随机出现段错误,因为您之前做了一些非法的事情。不要那样做。 ;)

如果您的程序在某处写入越界,任何事情都可能发生。它可能会碰到一个未分配的页面,在这种情况下你会得到一个段错误。或者它可能会在分配给您的进程的页面上遇到未使用的数据,在这种情况下,它不会产生任何实际影响(除非它在之后正确初始化,覆盖您的第一个非法写入,然后您尝试从中读取,期望原始(无效)值仍然存在。或者它可能会命中实际使用的数据,在这种情况下,您稍后会在程序尝试读取该数据时遇到错误。

读取数据时存在几乎相同的场景。您可能很幸运并立即遇到段错误,或者您可以访问未使用和未初始化的内存,并读取垃圾数据(这很可能会在以后使用该数据时导致错误),或者您可以从内存地址读取已经在使用(这也会给你垃圾)。

所以是的,这些错误很难找到。我能给出的最好建议是 1)在你的代码中撒上断言,以确保维护基本的不变量,以及 2)逐步执行程序,并且在每一步,验证你没有读取或写入不'的地址'不属于你。

MSVC 默认启用了一个安全 SCL 选项,它将对 STL 代码执行边界检查,这有助于发现此类错误。

我相信 GCC 可以选择执行类似的操作(但默认情况下未启用)。

段错误令人讨厌。一旦人们多次被这样的错误所困扰,他们往往会变得更加自律,避免越界访问内存。 :)

【讨论】:

  • 我听从了您的建议,现在正在使用 Code::Blocks。添加了调用堆栈!
  • 在《C++ For Dummies》一书中作者推荐它。
  • +1 segfault on any system (Window, Unix(like), OpenVMS“很久以前”发生在崩溃之前。
  • 我想我只是找到了问题所在,但不知道如何解决它:(
【解决方案2】:

您可以尝试Valgrind 来帮助找到问题。鉴于您在问题中提出的行,我不得不猜测您已经损坏了堆,或者您遇到了堆栈问题。

【讨论】:

  • Valgrind 只有 Linux,而且 OP 听起来他在使用 Windows。
  • 原帖中的 Windows 怎么说?开发-C++?我不熟悉它,但一个快速的谷歌说它是 Windows 或 Linux。只是Linux上没有人真正使用它吗?
  • 是的,对不起,我实际上使用的是 Windows :)。
  • 射击。好的,所以 Valgrind 出局了。我找到了这个帖子,显然有一些答案:lists-archives.org/mingw-users/04129-best-memory-checker.html
  • 问题显然被发现..我不明白为什么它会导致段错误并且不知道如何解决这个问题,因为我真的需要在每个周期中擦除地图!
【解决方案3】:

显而易见的问题是“_p 是什么”。在调试器中,您应该能够查看调用堆栈。跟随 _p 回到它的原点。确认它的大小正确,它没有被删除,并且它确实存在。

如果这并不容易,总会有一些蛮力的方法来注释掉随机(可疑的)代码,直到它工作或返回并与已知的工作副本进行比较。

【讨论】:

    【解决方案4】:

    这是非常模糊的,所以几乎不可能回答。一个明显的建议是检查您是否正在初始化所有变量。一些编译器会在调试中清零您未初始化的内容,而不是在发布时这样做,从而导致随机和意外结果。

    【讨论】:

      【解决方案5】:

      您可以在程序开头使用_CrtSetDbgFlag() 来启用一些堆调试选项。另见The CRT Debug Heap。这可以帮助您追踪您在哪里做坏事的记忆。它在 Microsoft 的 C 运行时中可用,MinGW 编译器默认链接到它。如果您改用 GNU 的 C 运行时,那么您将无法走这条路。

      【讨论】:

        【解决方案6】:

        与 stl_construct.h 相比,问题更可能出在您的代码中。我假设该文件是 Dev-C++ 的 STL 发行版的一部分。分段错误可能发生在使用 stl_construct.h 中的模板实例化的代码中,但问题的根本原因将在其他地方。我会尝试通过在崩溃时获取堆栈跟踪来解决这个问题。对于堆栈跟踪中的每个函数(尤其是新编写的函数),尝试检查代码并查找以下类型的可能错误:

        • 未初始化的变量(尤其是用于数组访问的索引)
        • 释放或删除后使用的内存。
        • 数组边界外的数组访问
        • 在未检查 NULL 的情况下使用的内存分配(如果使用 new 则没有问题,因为它不返回 NULL,它会引发异常)

        【讨论】:

          【解决方案7】:

          您的描述和调用堆栈表明程序在静态变量初始化期间崩溃。这是 C++ 的一个常见缺陷:没有简单的方法来控制静态变量的初始化顺序。

          例如,你的程序可能有对象foo,它依赖于对象bar;但可能是程序在构造bar 之前调用了foo 的构造函数。那会很糟糕,并且可能会导致您描述的那种问题。 (CheckingInstructionsThread 是生成线程的静态变量吗?这可能就是问题所在。)

          要解决此问题,您可能需要查看程序的 .cpp 文件中的静态变量(包括类静态变量和全局变量),尤其是某些类类型的变量。修改构造函数以将一些跟踪写入 stderr 可能有帮助,也可能没有帮助;如果您这样做,请使用 fprintf 而不是 cerr

          编辑:如果您不确定它是否与静态有关,请尝试在main() 的开头添加这样的一行:

           fprintf(stderr, "main() entered\n");
          

          这不排除静态初始化是问题的原因;即使它在main() 之前没有崩溃,您仍然可能会错误地设置数据结构。但是,如果您从未使用过 fprintf,那么您知道静态初始化是原因。

          【讨论】:

          • 那么,它已经通过了静态初始化步骤;但正如我所写,这并不排除静态初始化是问题所在。 你的程序中有类静态变量、文件静态变量或全局变量吗?有没有非平凡的构造函数?
          【解决方案8】:

          使用核心文件运行调试器后,堆栈跟踪会告诉您什么? 以正常方式运行 gdb gdb a.out

          然后检查核心文件

          核心 a.out.core

          看看堆栈

          bt

          【讨论】:

            【解决方案9】:

            这很可能与无效的迭代器有关 - 搜索您迭代容器的位置和/或将迭代器保留在容器中并同时删除/插入元素。
            正如其他所有人所指出的 - 使用调用 -堆栈以找到确切的位置。

            【讨论】:

              【解决方案10】:

              当您使用经过彻底测试但不属于您的代码时,您必须做的第一件事就是向上调用堆栈,直到您最终使用您自己的代码,您将在其中找到导致此问题的信息发生。

              在调用堆栈中,最重要的查看位置是您的代码(调用者)和传递给您调用的函数(被调用者)的参数。

              如果没有这些信息,我们将无法为您提供更多帮助。 ;-)

              有关分段错误的更多信息:http://en.wikipedia.org/wiki/Segmentation_fault
              (必读,另见“另见”和“外部链接”的链接)

              【讨论】:

              • 添加了调用堆栈(见编辑说明)。因此,如果我理解问题可能来自 VehicleManager::get_vehicles_distances ?
              【解决方案11】:

              地图不是很友好的线程。如果您在线程代码中执行映射操作,您确实需要锁定对该映射的所有访问,并意识到您可能持有的任何迭代器都可能无效。

              【讨论】:

              • 看来问题实际上来自那里。我试图锁定对车辆距离的线程访问(地图)但没有成功:(
              • 我不使用boost,所以它可能有这样的东西:创建一个类似于locked的模板化包装类,它包含一个互斥锁和数据类型本身。数据类型的更改将导致所有消费代码中断,您可以使用它来查找任何和所有访问以添加锁定/访问/解锁并检查使用的数据的副本是否正在复制而不是引用。如果禁用了主题,程序会运行吗?
              【解决方案12】:

              它看起来像一个危险的功能:)

              但有一件事让我印象深刻。分配的内存去哪儿了?直观地说,我希望有一个指向指针的指针作为第一个参数,然后取消引用它。像这样:

              template<typename _T1, typename _T2>
                  inline void
                  _Construct(_T1** __p, const _T2& __value)
                  {
              
                     ::new(static_cast<void*>(*__p)) _T1(__value);
                  }
              

              或者,对指针的引用:

              template<typename _T1, typename _T2>
                  inline void
                  _Construct(_T1*& __p, const _T2& __value)
                  {
              
                     ::new(static_cast<void*>(__p)) _T1(__value);
                  }
              

              【讨论】:

                【解决方案13】:

                调试器应该让你向上调用堆栈。这样,您应该能够在自己的代码中看到导致 seg 错误的位置。

                【讨论】:

                • 但是,问题可能发生在过去的某个时间。到实际崩溃发生时,堆栈中可能没有任何有用的信息。
                猜你喜欢
                • 2019-06-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2020-11-05
                • 1970-01-01
                • 1970-01-01
                • 2020-11-05
                相关资源
                最近更新 更多