【问题标题】:Linux Segmentation fault with std::string::iterator带有 std::string::iterator 的 Linux 分段错误
【发布时间】:2013-12-24 18:59:46
【问题描述】:

我在 CentOS 6.4 64 位机器上的 libc.so.6 中不断遇到异常的分段错误。这是 gdb 最常报告的回溯:

0x00007ffff60d9b3f in memcpy () from /lib64/libc.so.6
(gdb) backtrace
#0  0x00007ffff60d9b3f in memcpy () from /lib64/libc.so.6
#1  0x00000000004b6a6b in std::string::_S_construct<__gnu_cxx::__normal_iterator<char*, std::string> > ()
#2  0x00000000004b719b in NewsMAIL::SMTPClient::receiveLine(std::basic_string<char, std::char_traits<char>, std::allocator<char> >*) ()
#3  0x00000000004b776f in NewsMAIL::SMTPClient::handleResponse() ()

这是似乎触发段错误的代码:

bool SMTPClient::receiveLine(std::string* Line)
{
    static std::string Buffer;
    std::string::iterator iter;

    while((iter = std::find(Buffer.begin(), Buffer.end(), '\n')) == Buffer.end()) {

        char Bucket[MAX_BUCKET_SIZE + 1] = {};
        int BytesRecv = read(m_Socket, Bucket, MAX_BUCKET_SIZE);

        //Did we get a socket error?
        if(BytesRecv == -1) {

            //This is generally considered a bad thing..
            *Line = Buffer;
            Buffer = std::string("");
            return false;
        }

        Bucket[BytesRecv] = 0;
        Buffer += Bucket;
    }

    *Line = std::string(Buffer.begin(), iter);
    Buffer = std::string(iter + 1, Buffer.end());

    return true;
}

有时它可以 100% 工作而没有任何故障,因此不幸的是并非每次都如此。 上面的代码是一个稍微修改过的版本:https://stackoverflow.com/a/1584620/3133245

有人对为什么会发生这种情况有任何想法吗?我正在用 g++ 4.7.2 编译

谢谢!

内特

【问题讨论】:

  • 除了您有两个答案之外,字符串的operator+= 可以使任何迭代器无效。想象一下,如果需要调整字符串的内部缓冲区大小,就会发生这种情况。

标签: c++ string sockets iterator segmentation-fault


【解决方案1】:

使用静态变量(缓冲区)不是线程安全的。可能会导致崩溃。

您应该检查Line 不是NULL

顺便说一句,Buffer = std::string(""); 可能是Buffer.clear();

【讨论】:

  • 虽然应用程序是多线程的,但每个线程都有自己的此类实例,因此静态 std::string 永远不会越过线程边界。但是,我确实将其更改为 Buffer.clear() - 感谢您的建议。
  • 这是你的错误 - 静态变量。 2个线程不能同时调用这个函数。它们都操作同一个对象(缓冲区)。您应该使用关键部分保护此功能。
  • Egur 我觉得自己像个白痴,谢谢你弄清楚这一点——它现在完美无缺:)
  • 没问题。这是一个常见的错误。
【解决方案2】:

除了静态变量问题,您确定接收到的数据不包含嵌入的 NULL 字符吗?

如果生成的 Buffer 包含嵌入的 NULL 字节,则此行将不会使用 += 运算符进行正确的连接:

Buffer += Bucket;

+= 重载假定 Bucket 是 c 风格的字符串,因此遇到的第一个 NULL 字节将在连接发生时用作终止符。

看一眼代码,似乎是这样的情况,如果 Bucket 确实包含嵌入的 NULL 字符,执行上述连接可能会导致您的“iter”迭代器指向通过 Buffer 的 end()(在while() 循环之后的那些行)。

相反,您可以这样做:

Buffer.append(Bucket, BytesRecv)

这保证 Bucket 正在寻址的所有字符都将连接到现有字符串上。

但在进行任何更改之前,请确保您确切知道问题所在,尤其是因为您表示该错误不会经常发生。在不知道错误真正原因的情况下更改代码可能只会掩盖错误,从而使诊断真正问题变得更加困难。

【讨论】:

  • 感谢 Paul 的建议,关于在不逐字节接收的情况下不使用静态缓冲区的任何建议?还是我只是担心逐字节真的效率低下?
  • 好吧,你可以让缓冲区成为 SMTPClient 类的非静态成员——这将确保缓冲区不会相互踩踏。但是最后,如果这是您的目标,您将需要弄清楚如何将多个缓冲区合并为一个缓冲区。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
  • 1970-01-01
  • 2013-04-28
  • 2012-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多