4

pthreads を使用して C++ の Linux で Web サーバーを作成しています。リークとメモリの問題についてvalgrindでテストしました-すべて修正されました。スレッドの問題について helgrind でテストしました-すべて修正されました。ストレステストを試みています。probram を helgrind で実行すると問題が発生します

valgrind --tool=helgrind ./chats

で殺したときと同じように、「殺されました」というテキストでランダムな場所で死ぬだけkill -9です。helgrind からときどき受け取る唯一の報告は、プログラムがいくつかのロックを保持したまま存在しているということです。

漏れをチェックするとき:

valgrind  --leak-check=full ./chats

より安定していますが、数百の同時接続で一度停止させることができました。

プログラムを単独で実行してみましたが、まったくクラッシュしませんでした。最大 250 の同時接続を試しました。各スレッドは 100 ミリ秒で遅延し、同時に複数の接続を容易にします。クラッシュはありません。

いずれの場合も、スレッドと接続は 10 を超えず、2 つの接続でもクラッシュすることがわかりますが、同時に 1 つの接続だけでは決してクラッシュしません (メイン スレッドと 1 つのヘルパー スレッドを含めると合計 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. シグナルをリッスンしてシグナルを送信する 1 つのヘルパー スレッド。アプリのシグナル受信を停止し、シグナル キューを手動でポーリングします。その理由は、スレッドを使用するとシグナルを処理するのが難しいためです。私はこのテクニックをスタックオーバーフローで見つけましたが、他のプロジェクトでもうまく機能するようです。
  3. クライアント接続スレッド

完全なコードはかなり大きいですが、誰かが興味を持っている場合はチャンクを投稿できます。

アップデート:

接続が1つだけで問題を引き起こすことができました。それはすべてクライアントスレッドで起こっています。これが私がすることです:

  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 は、シグナルと sigfault についてすべて教えてくれます。

于 2013-11-04T17:24:49.003 に答える