4

我正在使用 pthreads 在 C++ 中的 linux 上创建一个 Web 服务器。我用 valgrind 测试了它的泄漏和内存问题 - 全部修复。我用 helgrind 测试了它的线程问题 - 全部修复。我正在尝试压力测试。当 probram 与 helgrind 一起运行时,我遇到了问题

valgrind --tool=helgrind ./chats

它只是在带有“已杀死”文本的随机位置上死亡,就像我用 . 杀死它时一样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;
}

PS线程是:

  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:如何知道远程端在写入时断开连接。读取时选择返回有数据但读取的数据为0。写入怎么样?

4

1 回答 1

2

好吧,我只需要处理 SIGPIPE 信号并写入返回的 -1 -> 我关闭套接字并优雅地退出线程。奇迹般有效。

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

signal(SIGPIPE, SIG_IGN);

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

gdb ./your-program
> run

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

于 2013-11-04T17:24:49.003 回答