UNIXネットワークプログラミング——常用サーバーモデルのまとめ

4917 ワード

次の9つのサーバモデルは、次のとおりです.
  • 反復サーバ.
  • 同時サーバは、クライアントforkごとに1つのプロセスです.
  • は、各サブプロセスがacceptを呼び出し、acceptはロック保護されていない.
  • は、acceptをファイルロックで保護するために、あらかじめ子供プロセスを派遣する. 
  • は、acceptをスレッド反発ロックロックで保護するために、あらかじめ子供プロセスを派遣する.
  • は、子プロセスを事前に派遣し、親プロセスから子プロセスにソケットインタフェース記述語を伝達する. 
  • 同時サーバで、クライアントリクエストごとにスレッドを作成します.
  • スレッドを事前に作成し、acceptを反発ロックロックで保護します. 
  • スレッドを事前に作成し、メインスレッドからacceptを呼び出し、各クライアント接続をスレッドプール内の使用可能なスレッドに渡します.

  • 1、反復サーバー
    標準コード:
    socket
    bind
    listen
    for(;;)
    {
    connfd = accept(listenfd, (SA*)&cliaddr, &clilen);
    process_connection(connfd);
    close(connfd);
    }

    利点:主にプログラミングが簡単です.
    欠点:1つの接続を処理した後でやっと次の接続を処理することができて、同時性がなくて言えて、応用は少ないです.
    2.同時サーバー、顧客forkごとに1つのプロセス
    標準コード:
    init_address(server_addr)
    listenfd = socket(AF_INET,SOCKET_STREAM,0);
    bind(listenfd, (SA*)server_addr, sizeof(serveraddr));
    listen(listenfd,BACKLOG);
    for(;;)
    {
    connfd = accept(listenfd, (SA*)&cliaddr, &clilen);
    if(connfd < 0)
    {
    if(EINTR == errno)
    continue;
    else
    //error
    }
    
    if(fork() == 0)
    {
    close(c=listenfd);
    process_connection(connfd); //child process
    exit(0);
    }
    close(connfd);//parent, close connected socket
    }

    初期のネットワーク・サーバが毎日数百または数千のクライアント接続を処理していた場合、このサーバ・モデルは対応していました.
    しかし、インターネット業務の急速な発展に伴い、忙しいウェブサーバは毎日千万以上の接続を処理する必要があり、
    サーバを送信する問題は、クライアントフィールドforkごとにサブプロセスを比較するのにcpu時間を消費することです.
    3.事前に出産プロセスを派遣し、各サブプロセスはacceptを呼び出し、acceptはロック保護されていない
    欠点:
  • サーバは、起動時に予め派生するサブプロセスがどれだけ必要かを判断しなければならない
  • .
  • 驚異的な現象(接続が到来してすべての傍受プロセスを起動する)が、新しいバージョンのlinuxよりこの問題
  • を修正したようだ.
    利点:親プロセス実行forkのオーバーヘッドを導入することなく、新しい顧客を処理できます.
    4.あらかじめ出産プロセスを派遣し、ファイルロックでacceptを保護する
    本モデルと3の違いはaccept(listenfd)に対してファイルロックを使用しただけである.
    このモデルは,ライブラリ関数として実装されたacceptが複数のプロセスで同じリスニングセットインタフェースを参照できないことを解決するためである.
    問題(BSDのunixに由来し、カーネルで実装されたacceptは参照できる).ファイルロックを使用すると、各接続が保証され、
    accept呼び出しにプロセスがブロックされています.
    カーネル内でacceptが実装されているシステムでは、このモデルは少なくともロック解除のオーバーヘッドを増加させるため、
    第3のモデルは性能が低い(特に驚くべき問題を解消したシステム上で)
    5.あらかじめ出産プロセスを派遣し、スレッド反発ロックロックでacceptを保護する
    標準コード:
    static pthread_mutex_t *mptr;
    void
    my_lock_init(char *pathname)
    {
    int fd;
    pthread_mutexattr_t mattr;
    // /dev/zero 
    // mmap 
    // /dev/zero 
    fd = open("/dev/zero", O_RDWR, 0,);
    //  mptr 
    mptr = mmap(0, sizeof(pthread_mutex_t), PORT_READ | PORT_WRITE, MAP_SHARED, fd, 0);
    close(fd);
    pthread_mutexattr_init(&mattr);
    pthread_mutexattr_setpshared(&mptr, PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(mptr, &mattr);
    }
    
    void
    my_lock_wait()
    {
    pthread_mutex_lock(mptr);
    }
    
    void
    my_lock_release()
    {
    pthread_mutex_unlock(mptr);
    }
    
    int
    main(int argc, char **argv)
    {
    //init socket and address
    my_lock_init(pathname);
    for(i = 0; i < nchildren; ++i)
    {
    pids[i] = child_make(i, listenfd, addrlen);
    }
    
    for(;;)
    pause();
    }
    
    pid_t
    child_make(int i, int listenfd, int addrlen)
    {
    pid_t pid;
    
    if((pid = fork) > 0)
    return pid;
    
    child_main(i, listenfd, addrlen);
    }
    
    void
    child_main()
    {
    for(;;)
    {
    my_lock_wait();
    connfd = accept(listenfd, chiladdr, &clilen);
    my_lock_release();
    web_child(connfd);
    close(connfd);
    }
    }

    このモデルは、ファイルロックによる保護がファイルシステムに関連するため、モデル4においてさらに改良されている.
    時間がかかる可能性があるので、改善された方法は、ファイルロックの代わりにpthread mutex反発量を使用することです.スレッドロックによるacceptの保護
    同じプロセス内の各スレッドのロックだけでなく、異なるプロセス間のロックにも適用されます.
    マルチプロセス環境でスレッド反発ロックを使用して同期を実現するには、次の2つの要件があります.
    1.反発ロックは、すべてのプロセスによって共有されるメモリ領域に配置する必要があります.
    2.スレッドライブラリが異なるプロセス間で共有される反発ロックであることを通知する必要があります.
    注意:現在人気のある高性能ウェブサーバの代表nginxはこのモデルを採用しており、ネットワーク上ではnginxの研究が多い.
    6.子プロセスを事前に派遣し、親プロセスから子プロセスにソケットインタフェース記述語を渡す
    利点:acceptのロック保護は不要
    劣勢:
  • は複雑な符号化を行います.親プロセスは、アイドルサブプロセスに新しいインタフェースを渡すために、サブプロセスのアイドル状態を追跡する必要があります.前述の事前出産プロセスの例では、親プロセスは、どのサブプロセスが顧客接続を受信するかに関心を持つ必要はなく、オペレーティングシステムはスケジューリングアルゴリズムに基づいてこれらの詳細を処理します.このモデルを用いた結果,これらのプロセスは接続を均衡的に処理できない.
  • 親プロセスは、バイトフローパイプを介して記述子を各サブプロセスに渡し、各サブプロセスは、共有メモリ領域における反発ロックを使用するよりも、ファイルロックを使用して実施されるロックおよびロック解除を使用するよりも、バイトフローパイプを介して単一のバイトを書き戻すのに時間がかかる.

  • 7.顧客要求ごとにスレッドを作成する同時サーバー
    標準コード:
    for(;;)
    {
    connfd = accept(listenfd, cliaddr, &clilen,);
    pthread_create(&tid, NULL, &doit, (void *)connfd);
    }
    
    void *
    doit(void *arg)
    {
    pthread_detach(pthread_self());
    web_child((int)arg);
    close((int)arg)
    return (NULL);
    }

    利点:符号化が簡単です.
    欠点:フィールドで各接続にスレッドを作成するのは、事前に派生したスレッドプールに比べて時間がかかります.
    8.あらかじめスレッドを作成し、acceptを反発ロックロックで保護する
    メリット:
  • プログラミングの概要、
  • を理解しやすい
  • スレッドプール方式により、フィールド作成スレッドのオーバーヘッド
  • を回避
  • OSスレッドスケジューリングアルゴリズムはスレッド負荷の均衡性
  • を保証する.
    これがleader-followerモードのスレッドが接続を待っていて、他のスレッドがスリープしています.新しい接続が来たらリーダーが接続を処理し、解放します.
    listenfdを放して、他のスレッドがリスニングセットインタフェースlistenfdを競合します(驚くべき問題があるかもしれません).リーダーは接続を処理した後followerになります.
    9.事前にスレッドを作成し、メインスレッドからacceptを呼び出し、各クライアント接続をスレッドプールの使用可能なスレッドに渡す
    劣勢:モデル8に対して、このモデルはpthread mutexを使用するだけでなく、pthread condを使用する必要がある.
    転載先:https://www.cnblogs.com/hehehaha/p/6332533.html