1

私は現在このプロジェクトをCでのみ行っているので、これまではWebサーバーをシングルスレッドアプリケーションとしてのみ使用してきました。しかし、私はもうそれを望んでいません!だから私は私の仕事を処理する次のコードを持っています。

void BeginListen()
{
        CreateSocket();

        BindSocket();

        ListenOnSocket();

        while ( 1 )
        {
            ProcessConnections();
        }
}

fork();これで、開始前に追加しました。これはProcessConnection();、複数の接続を許可するのに役立ちます。ただし、この回答にあるアプリケーションをデーモン化するためのコードを追加すると、少し問題が発生しました。を使用fork()すると、実行中のアプリ全体のコピーが作成されます。これがの目的ですfork()。そこで、この問題を解決したいと思います。

ProcessConnection()はこのように見えます

void ProcessConnections()
{
        fork();

        addr_size = sizeof(connector);

        connecting_socket = accept(current_socket, (struct sockaddr *)&connector, &addr_size);

        if ( connecting_socket < 0 )
        {
                perror("Accepting sockets");
                exit(-1);
        }

        HandleCurrentConnection(connecting_socket);


        DisposeCurrentConnection();
}

connecting=socket = accept一度に複数の接続を受け入れるようにするために、単に上または後に数行を追加するにはどうすればよいですか?使用できますfork();が、DisposeCurrentConnection();結局のところ、そのプロセスを強制終了して、親スレッドを実行したいだけです。

4

5 に答える 5

4

私はあなたが何をしようとしているのか100%確信が持てません. ただし、子プロセスが終了したときに SIGCHLD シグナルに反応する必要があることに注意してください。そうしないと、大量のゾンビプロセスがぶらぶらして、親プロセスに終了ステータスを渡すのを待っています。C疑似コード:

for (;;) {
  connecting_socket = accept(server_socket);
  if (connecting_socket < 0)
    {
      if (errno == EINTR)
        continue;
      else
        {
          // handle error
          break;
        }
    }

  if (! (child_pid = fork ()))
    {
       // child process, do work with connecting socket
       exit (0);
    }
  else if (child_pid > 0)
    {
      // parent process, keep track of child_pid if necessary.
    }
  else
    {
      // fork failed, unable to service request, send 503 or equivalent.
    }
}

child_pid は、(前述のように) 子プロセスを強制終了するために必要ですが、waitpid を使用して終了ステータスを収集する場合にも必要です。

ゾンビ プロセスに関して、プロセスに何が起こったのか興味がない場合は、SIGCHLD のシグナル ハンドラをインストールし、次のように、子プロセスがなくなるまで -1 を指定して waitpid をループすることができます。

while (-1 != waitpid (-1, NULL, WNOHANG))
  /* no loop body */ ;

waitpid 関数は、終了した子プロセスの pid を返すので、必要に応じて、これを接続に関する他の情報と関連付けることができます (pid を追跡している場合)。SIGCHLD がキャッチされた場合、accept はおそらく errno が EINTR に設定された状態で終了し、有効な接続がないことに注意してください。

編集:
エラー状態を確認することを忘れないでください。つまり、fork は -1 を返します。

于 2009-01-11T19:33:43.807 に答える
2

unix で fork() とスレッドについて話すことは、厳密には正しくありません。Fork は、親との共有アドレス空間を持たないまったく新しいプロセスを作成します。

NCSA httpd や Apache 1.x などの従来の UNIX Web サーバーによく似た、要求ごとのプロセス モデルを実現しようとしていると思います。あるいは、共有グローバル メモリを使用してマルチスレッド サーバーを構築しようとしている可能性もあります。

リクエストごとのプロセス サーバー:

fork() を呼び出すと、システムはファイル記述子を含む親プロセスのクローンを作成します。これは、ソケット要求を受け入れてから fork できることを意味します。子プロセスには、応答してから終了できるソケット要求があります。

プロセスのメモリは物理的にコピーされず、ページはプロセス間で共有されるため、これは UNIX では比較的効率的です。システムは、コピー オン ライトと呼ばれるメカニズムを使用して、子プロセスがメモリに書き込むときにページごとにコピーを作成します。したがって、unix でのリクエストごとのプロセス サーバーのオーバーヘッドはそれほど大きくなく、多くのシステムがこのアーキテクチャを使用しています。

于 2009-01-11T19:42:05.350 に答える
1

1 つのプログラムでさまざまな要求をリッスンして接続できるようにする select() 関数を使用することをお勧めします..ブロッキングは回避されますが、フォークはプログラムのコピー用の新しいアドレス空間を作成し、メモリの非効率性につながります....

select(Max_descr, read_set, write_set, exception_set, time_out);

つまり、できます

fd_set* time_out;
fd_set* read_set;
listen(1);
listen(2);
while(1)
{
  if(select(20, read_set, NULL,NULL, timeout) >0)
  {
    accept(1);
    accept(2); .....
    pthread_create(func);....
  }
  else
}
于 2010-11-16T08:34:46.033 に答える
0

fork()の戻り値を確認してください。ゼロの場合、あなたは子プロセスであり、作業を行った後にexit()を実行できます。正の数の場合は、新しく作成されたプロセスのプロセスIDです。これにより、子プロセスが何らかの理由で長時間ぶらぶらしている場合に、子プロセスをkill()することができます。

于 2009-01-11T19:27:21.323 に答える
0

私のコメントによると、このサーバーは実際にはマルチスレッドではなく、マルチプロセスです。

複数の接続を受け入れる簡単な方法が必要な場合 (そしてパフォーマンスをあまり気にしない場合) は、inetdで機能させることができます。これにより、プロセスを生成して inetd のデーモンになる作業が残り、単一の接続を処理して処理するプログラムを作成するだけで済みます。編集: または、これがプログラミング演習である場合は、inetd のソースを取得して、それがどのように機能するかを確認できます

select を使用して、スレッドや新しいプロセスを使用せずにやりたいことを実行することもできます。

select の使用方法を説明する記事を次に示します (フォークやスレッドに比べてオーバーヘッドがかなり低い -この方法で作成された軽量 Web サーバーの例を次に示します)。

また、これを C で行うことに慣れておらず、C++ でも問題ない場合は、コードを移植してACEを使用することを検討してください。これは、ほとんどすべての接続処理モデルをサポートし、非常に移植性が高いと信じているため、これを行う方法の設計パターンを探すのにも適しています。

于 2009-01-11T19:42:23.787 に答える