【问题标题】:valgrind/helgrind gets killed on stress testvalgrind/helgrind 在压力测试中被杀
【发布时间】:2013-11-15 05:18:03
【问题描述】:

我正在使用 pthreads 用 C++ 在 linux 上创建一个 Web 服务器。我用 valgrind 测试了它的泄漏和内存问题 - 全部修复。我用 helgrind 测试了它的线程问题 - 全部修复。我正在尝试stress test。使用 helgrind 运行 probram 时出现问题

valgrind --tool=helgrind ./chats

它只是在带有“Killed”文本的随机位置死亡,就像我用kill -9 杀死它时一样。有时我从 hlgrind 得到的唯一报告是程序存在时仍然持有一些锁,这在被杀死时是正常的。

检查泄漏时:

valgrind  --leak-check=full ./chats

它更稳定,但我设法让它在数百个并发连接的情况下死掉一次。

我尝试单独运行程序,但根本无法让它崩溃。我尝试了多达 250 个并发连接。每个线程延迟 100 毫秒,以便更容易同时拥有多个连接。没有崩溃。

在所有情况下,线程和连接都不会超过 10,即使有 2 个连接,我也看到它崩溃,但绝不会同时只有一个连接(包括主线程和一个辅助线程,总共 3 个) .

  1. 是否有可能只有在运行时才会出现问题 是 helgrind 还是只是 helgrind 让它更有可能展示?
  2. 程序被杀死(被内核杀死)的原因是什么?分配了太多的内存、太多的文件描述符?

我测试了更多,我发现它只有在客户端超时并关闭连接时才会死机。所以这里是检测客户端关闭套接字的代码:

void *TcpClient::run(){
  int ret;
  struct timeval tv;
  char * buff = (char *)malloc(10001);
  int br;

  colorPrintf(TC_GREEN, "new client starting: %d\n", sockFd);
  while(isRunning()){
    tv.tv_sec = 0;
    tv.tv_usec = 500*1000;
    FD_SET(sockFd, &readFds);
    ret = select(sockFd+1, &readFds, NULL, NULL, &tv);
    if(ret < 0){
      //select error
      continue;
    }else if(ret == 0){
      // no data to read
      continue;
    }
    br = read(sockFd, buff, 10000);
    buff[br] = 0;

    if (br == 0){
    // client disconnected;
      setRunning(false);
      break;
    }

    if (reader != NULL){
      reader->tcpRead(this, std::string(buff, br));
    }else{
      readBuffer.append(buff, br);
    }
    //printf("received: %s\n", buff);

  }
  free(buff);

  sendFeedback((void *)1);
  colorPrintf(TC_RED, "closing client socket: %d\n", sockFd);
  ::close(sockFd);
  sockFd = -1;

  return NULL;
}
// this method writes to socket
bool TcpClient::write(std::string data){
  int bw;
  int dataLen = data.length();

  bw = ::write(sockFd, data.data(), dataLen);
  if (bw != dataLen){
    return false; // I don't close the socket in this case, maybe I should
  }
  return true;
}

附:主题是:

  1. 主线程。此处接受连接。
  2. 一个监听信号和发送信号的辅助线程。它停止应用程序的信号接收并手动轮询信号队列。原因是使用线程时很难处理信号。我在 stackoverflow 中发现了这种技术,它在其他项目中也能很好地工作。
  3. 客户端连接线程

完整的代码相当大,但如果有人感兴趣,我可以发布块。

更新:

我设法仅通过一个连接触发了该问题。这一切都发生在客户端线程中。我就是这样做的:

  1. 我读取/解析标题。我在写入之前设置了延迟,以便客户端可以超时(这会导致问题)。
  2. 此处客户端超时并离开(可能关闭套接字)
  3. 我写回标题
  4. 我写回了 html 代码。

这是我写回的方式

  bw = ::write(sockFd, data.data(), dataLen);
  // bw is = dataLen = 108 when writing the headers
  //then secondary write for HTML kills the program. there is a message before and after write()
  bw = ::write(sockFd, data.data(), dataLen); // doesn't go past this point second time

更新 2:知道了 :)

gdb 赛斯:

Program received signal SIGPIPE, Broken pipe.
[Switching to Thread 0x41401940 (LWP 10554)]
0x0000003ac2e0d89b in write () from /lib64/libpthread.so.0

问题1:我应该怎么做才能取消接收到这个信号。 问题2:如何知道远程端在写入时断开连接。读取时select返回有数据,但读取的数据为0,那么写入呢?

【问题讨论】:

  • 您查看过您的日志/dmesg 吗?听起来它已经被 OOM 杀死了
  • 已检查。 dmesg -> 没有,系统日志 -> 没有。嗯,在 dmesg 中有一个段错误,但我记得在开发的早期阶段,它已经修复了。我设法跟踪问题并重复它 - 阅读有问题的更新
  • Valgrind 有时会这样死去,np 似乎知道原因。这可能是内核错误/功能。可以试试不同系列的内核吗?
  • 我可以,但我发现了问题。这次不是 valgrind,是 SIGPIPE 信号。我以为我处理了所有终止程序的信号,但我忘记了 SIGPIPE。

标签: c++ linux multithreading tcp valgrind


【解决方案1】:

好吧,我只需要处理 SIGPIPE 信号并写入返回的 -1 -> 我关闭套接字并优雅地退出线程。像魅力一样工作。

我想最简单的方法是将 SIGPIPE 的信号处理程序设置为 SIG_IGN:

signal(SIGPIPE, SIG_IGN);

请注意,第一次写入是成功的,并没有杀死程序。如果您有类似的问题,请检查您是写一次还是多次。如果您不熟悉 gdb,请按照以下方法操作:

gdb ./your-program
> run

gdb 会告诉你所有关于信号和 sigfaults 的信息。

【讨论】:

    猜你喜欢
    • 2021-04-10
    • 2014-05-31
    • 2011-08-11
    • 2010-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-19
    • 2012-07-16
    相关资源
    最近更新 更多