【发布时间】: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 个) .
- 是否有可能只有在运行时才会出现问题 是 helgrind 还是只是 helgrind 让它更有可能展示?
- 程序被杀死(被内核杀死)的原因是什么?分配了太多的内存、太多的文件描述符?
我测试了更多,我发现它只有在客户端超时并关闭连接时才会死机。所以这里是检测客户端关闭套接字的代码:
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;
}
附:主题是:
- 主线程。此处接受连接。
- 一个监听信号和发送信号的辅助线程。它停止应用程序的信号接收并手动轮询信号队列。原因是使用线程时很难处理信号。我在 stackoverflow 中发现了这种技术,它在其他项目中也能很好地工作。
- 客户端连接线程
完整的代码相当大,但如果有人感兴趣,我可以发布块。
更新:
我设法仅通过一个连接触发了该问题。这一切都发生在客户端线程中。我就是这样做的:
- 我读取/解析标题。我在写入之前设置了延迟,以便客户端可以超时(这会导致问题)。
- 此处客户端超时并离开(可能关闭套接字)
- 我写回标题
- 我写回了 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