在多线程环境中使用时由 streambuf 触发的 SIGSEGV 错误

SIGSEGV fault triggered by streambuf when used in multithread environment

提问人:jackxie 提问时间:8/24/2022 更新时间:8/24/2022 访问量:126

问:

我实现了一个从 std::streambuf 派生的输出缓冲区 LogStreamBuf,

它用于具有大量 cout 的多线程环境,当短时间内大量输出时发生 SEGV 错误,

bt喜欢下面,复制时参数__n无效,在我看来,这似乎是线程安全问题,但我找不到解决方案来修复。

请任何建议提供帮助,非常感谢!

(gdb) bt
#0  0xb698ff64 in memcpy () at ../sysdeps/arm/armv7/multiarch/memcpy_impl.S:487
#1  0xb6b5a4dc in std::char_traits<char>::copy (__n=4294967295, __s2=0xb3ef984a "60\340", <incomplete sequence \333>, __s1=<optimized out>)
    at /home/tcwg-buildslave/workspace/tcwg-make-release_1/_build/builds/x86_64-unknown-linux-gnu/arm-linux-gnueabihf/gcc.git~linaro-7.5-2019.12-stage2/arm-linux-gnueabihf/libstdc++-v3/include/bits/char_traits.h:350
#2  std::basic_streambuf<char, std::char_traits<char> >::xsputn (this=0xa4db5c <gLogStreamBuf>, __s=0xb3ef984a "60\340", <incomplete sequence \333>, __n=2)
    at /home/tcwg-buildslave/workspace/tcwg-make-release_1/_build/builds/x86_64-unknown-linux-gnu/arm-linux-gnueabihf/gcc.git~linaro-7.5-2019.12-stage2/arm-linux-gnueabihf/libstdc++-v3/include/bits/streambuf.tcc:90
#3  0xb6b482c8 in std::basic_streambuf<char, std::char_traits<char> >::sputn (__n=<optimized out>, __s=<optimized out>, this=0xa4db5c <gLogStreamBuf>)
    at /home/tcwg-buildslave/workspace/tcwg-make-release_1/_build/builds/x86_64-unknown-linux-gnu/arm-linux-gnueabihf/gcc.git~linaro-7.5-2019.12-stage2/arm-linux-gnueabihf/libstdc++-v3/include/streambuf:451
#4  std::ostreambuf_iterator<char, std::char_traits<char> >::_M_put (__len=<optimized out>, __ws=<optimized out>, this=<synthetic pointer>)
    at /home/tcwg-buildslave/workspace/tcwg-make-release_1/_build/builds/x86_64-unknown-linux-gnu/arm-linux-gnueabihf/gcc.git~linaro-7.5-2019.12-stage2/arm-linux-gnueabihf/libstdc++-v3/include/bits/streambuf_iterator.h:282
#5  std::__write<char> (__len=<optimized out>, __ws=<optimized out>, __s=...)
    at /home/tcwg-buildslave/workspace/tcwg-make-release_1/_build/builds/x86_64-unknown-linux-gnu/arm-linux-gnueabihf/gcc.git~linaro-7.5-2019.12-stage2/arm-linux-gnueabihf/libstdc++-v3/include/bits/locale_facets.h:121
#6  std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<unsigned long> (this=<optimized out>, __s=..., __io=..., 
    __fill=<optimized out>, __fill@entry=32 ' ', __v=<optimized out>, __v@entry=60)
    at /home/tcwg-buildslave/workspace/tcwg-make-release_1/_build/builds/x86_64-unknown-linux-gnu/arm-linux-gnueabihf/gcc.git~linaro-7.5-2019.12-stage2/arm-linux-gnueabihf/libstdc++-v3/include/bits/locale_facets.tcc:933
#7  0xb6b483aa in std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::do_put (this=<optimized out>, __s=..., __io=..., __fill=32 ' ', __v=60)
    at /home/tcwg-buildslave/workspace/tcwg-make-release_1/_build/builds/x86_64-unknown-linux-gnu/arm-linux-gnueabihf/gcc.git~linaro-7.5-2019.12-stage2/arm-linux-gnueabihf/libstdc++-v3/include/bits/locale_facets.h:2515
#8  0xb6b4f8f2 in std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::put (__v=60, __fill=<optimized out>, __io=..., __s=..., 
    this=0xb6b94178 <(anonymous namespace)::num_put_c>)
    at /home/tcwg-buildslave/workspace/tcwg-make-release_1/_build/builds/x86_64-unknown-linux-gnu/arm-linux-gnueabihf/gcc.git~linaro-7.5-2019.12-stage2/arm-linux-gnueabihf/libstdc++-v3/include/bits/locale_facets.h:2376
#9  std::ostream::_M_insert<unsigned long> (this=0xa005f0 <std::cout@@GLIBCXX_3.4>, __v=60)
    at /home/tcwg-buildslave/workspace/tcwg-make-release_1/_build/builds/x86_64-unknown-linux-gnu/arm-linux-gnueabihf/gcc.git~linaro-7.5-2019.12-stage2/arm-linux-gnueabihf/libstdc++-v3/include/bits/ostream.tcc:73

LogStreamBuf 的实现

LogStreamBuf  gLogStreamBuf;

#define kInBufSize 48*1024

LogStreamBuf::LogStreamBuf()
{
    fInBuf = new char[kInBufSize];
    setbuf( fInBuf, kInBufSize );
    setp( fInBuf, (fInBuf + kInBufSize) );
    setg( fInBuf, fInBuf, (fInBuf + kInBufSize) );
    
}
void LogStreamBuf::RedirectStreams()
{
    cout << "Redirecting output to LogStreamBuf!"<<endl;
    cout.rdbuf(this); 
}

int LogStreamBuf::overflow( int ch )
{
    Lock();
    for(char *p=gptr(); p < epptr(); p++) {
        //  writes characters to the associated output sequence from the put area
    }
    setp( fInBuf, (fInBuf + InBufLen()) );
    setg( fInBuf, fInBuf, (fInBuf + InBufLen()) );
    Unlock();
}

int LogStreamBuf::sync()
{
    Lock();
    for(char *p=gptr(); p < pptr(); p++) {
        // sync characters pointer p to the associated output sequence from the put area
        count++;
    }
    gbump(count);
    Unlock();
}

int LogStreamBuf::underflow()
{
    setg( fInBuf, fInBuf, (fInBuf + InBufLen()) );
    return sgetc();
}
ostream& MacAddress::Print( ostream& ostrm ) const
{
    int oldFill = ostrm.fill();

    uint8 macAddress[6];

    Get(macAddress[0], macAddress[1], macAddress[2], 
        macAddress[3], macAddress[4], macAddress[5]);
    
    ostrm.fill( '0' );

    ostrm << hex 
          << setw(2) << right << (unsigned int) macAddress[0] << ':'
          << setw(2) << right << (unsigned int) macAddress[1] << ':'
          << setw(2) << right << (unsigned int) macAddress[2] << ':'
          << setw(2) << right << (unsigned int) macAddress[3] << ':'     <=====bt shows the problem starts from here    
          << setw(2) << right << (unsigned int) macAddress[4] << ':'
          << setw(2) << right << (unsigned int) macAddress[5]
          << dec;

    ostrm.fill(oldFill);

    return ostrm;
}

inline std::ostream& operator<<( std::ostream& ostrm, const MacAddress& ins )
{
    return ins.Print( ostrm );
}

cout << "Current Sucriber's MACaddr " << pSucriber->macAddress() << endl;  <<=== the cout sentence

err
Program terminated with signal SIGSEGV, Segmentation fault.
C++ 多线程 cout streambuf

评论

0赞 n. m. could be an AI 8/24/2022
请发布一个最小的可重现示例
1赞 G.M. 8/24/2022
做什么和做什么?为什么不使用 RAII 样式锁定,例如 ?Lock()Unlock()std::lock_guard
0赞 Sam Varshavchik 8/24/2022
此问题显示的代码无法满足 Stackoverflow 显示最小可重现示例的要求。正因为如此,这里的任何人都不太可能最终回答这个问题;但最多只能猜测。您需要编辑您的问题以显示一个最小的示例,不超过一两页代码(“最小”部分),其他人都可以完全按照所示剪切/粘贴、编译、运行和重现所描述的问题(“可重现”部分,这包括任何辅助信息,例如程序的任何输入)。有关详细信息,请参阅如何询问
0赞 Alan Birtles 8/24/2022
我不知道如果您更换了流缓冲区,线程安全保证是否仍然适用std::cout
0赞 jackxie 8/25/2022
@G.M. fMutex 是一个 POSIX 互斥锁,它是 LogStreamBuf 的成员,用于保护放置区域的字符齐平的输出序列。Lock() 和 Unlock() 操作此 fMutex 以保持输出序列一致。

答: 暂无答案