【问题标题】:Application crash on concurrent access to std::string并发访问 std::string 时应用程序崩溃
【发布时间】:2014-04-11 00:58:07
【问题描述】:

我有一个多线程应用程序,我使用 std::map 来存储键值对。虽然我使用互斥锁(pthread,因为不使用 C++11)保护对 std::map 的访问以确保互斥,但基于数据的密钥在互斥块之外。这是因为我的密钥制作是在本地完成的,而不是使用共享数据。我有一个小的实用函数 mkkey,它按值返回一个字符串,定义如下:

std::string mkkey(char *n1, char *n2, int d) {
  char buff[200];
  sprintf(buff, "%s:%s:%d", n1, n2, d);
  return buff;
}

mkkey 的参数不是 null 并且是有效的字符串,在大小限制内,所以我不会超过缓冲区大小。但是,我遇到了崩溃,pstack 核心显示以下内容: 对于一个线程:

 --- called from signal handler with signal 10 (SIGBUS) ---
 00271340 allocate__t24__default_alloc_template2b0i0Ui (30, 30, 10, 2, 1, 0) + a4
 0015ec84 __nw__Q2t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0_3RepUiUi (10, 20, 1, 2, 1, 1) + 14
 0015fa44 create__Q2t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0_3RepUi (19, 19, 664e10, fc27b694, ffbff790, fffc00) + 24
 0016c71c replace__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0UiUiPCcUi (fc27b888, 0, ffffffff, fc27b038, 19, 80808080) + 114
 002af8c4 assign__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCcUi (fc27b888, fc27b038, 19, fc27b051, 1, 1) + 24
 0029b380 assign__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCc (fc27b888, fc27b038, 7fffffe6, 664e10, fc27b051, 7fffffe6) + 24
 00263600 __t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCc (fc27b888, fc27b038, 7ffffc00, 664e10, fc27b895, 19) + 28
 0012caec mkKey__FRC8UserData (fc27b890, fc27b888, fc27b8a1, 0, ff000000, 80808080) + f4

对于另一个线程:

-----------------  lwp# 7 / thread# 7  --------------------
 0025b148 data__Q2t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0_3Rep (84f970, 4631bc, 1, 2, 1, 0) + 4
 001616c8 copy__Q2t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0_3RepUiPCcUi (84f970, 0, fbe7b0c8, 19, ffbff790, fffc00) + 24
 0016c7a8 replace__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0UiUiPCcUi (fbe7b918, 0, ffffffff, fbe7b0c8, 19, 80808080) + 1a0
 002af8c4 assign__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCcUi (fbe7b918, fbe7b0c8, 19, fbe7b0e1, 1, 1) + 24
 0029b380 assign__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCc (fbe7b918, fbe7b0c8, 7fffffe6, 664e10, fbe7b0e1, 7fffffe6) + 24
 00263600 __t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCc (fbe7b918, fbe7b0c8, 7ffffc00, 664e10, fbe7b925, 19) + 28
 0012caec mkKey__FRC8UserData (fbe7b920, fbe7b918, fbe7b931, 0, ff000000, 80808080) + f4

崩溃的线程似乎在通常表示访问无效地址的 SIGBUS 上崩溃。会出什么问题?由于 mkkey 按值返回而形成的 std::string 应该重新创建一个 std::string,但它表明 alloc 失败。它与std :: string有关吗?在使用少量数据进行调试时,我没有看到任何明显的问题,问题来自于更大的数据量。堆大小可能是导致同步分配失败的问题吗?还是 std::string 本身?我故意将 mkkey 保留在互斥锁之外,因为它是在本地完成的,而不是在共享数据上完成的。

【问题讨论】:

  • “堆大小可能是个问题吗?” - 不,但堆栈大小可以(在本文中描述的特定情况下)。
  • @barakmanos 在这种情况下,可能会尝试增加线程堆栈大小,因为我使用 pthread 分配的默认堆栈大小
  • 也许这些论点并没有您认为的那么有效?至少使用snprintf(当然,使用 C++ 范例会更好)。您能否尝试将您的程序精简为一个可以完整发布但仍会崩溃的最小示例?

标签: c++ string multithreading new-operator


【解决方案1】:

这是一种可能有很多原因的堆损坏,它是程序中可能发生的最丑陋的一种错误。它可能有很多原因,以至于没有必要列出它们。有一小段错误代码就足以覆盖其他东西的内存,例如通过过度索引或编写悬空指针......

以下是调试堆损坏的建议列表:How to debug heap corruption errors?

我自己通常使用特殊的分配器来调试它们,并将(非常慢的)全内存一致性检查调用放在我程序中的特定位置,这样我有时可以长时间搜索我的损坏发生的位置.

【讨论】:

  • 这仅限于一个特定的小模块,其中没有明显的内存泄漏(似乎是一个乏味的检查)。但是,线程堆栈大小(现在使用 pthread 给出的默认值)可能是一个问题。为了辩论起见,即使由于某些原因导致了一些内存覆盖,为什么新的内存分配会失败?
  • @DebasishJana 如果您覆盖属于分配器本身的内存区域(用于管理分配的块),那么分配器当然会在最坏的情况下失败或崩溃。也可能发生内存不足的情况,许多 C++ 模块在这种情况下会静默失败,尤其是在关闭异常处理而不是 badalloc 异常 new 只是返回 NULL 时。重要的不仅仅是你的小模块,在崩溃的进程中运行的每一小段代码都是可疑的(因为使用了相同的虚拟内存地址空间)。
猜你喜欢
  • 1970-01-01
  • 2021-10-02
  • 1970-01-01
  • 1970-01-01
  • 2017-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多