メッセージキューサーバーを実装するための最良の方法を研究するために最善を尽くした後、この質問をします。オペレーティングシステムが、プロセスとグローバルシステムが持つことができる開いているファイル記述子の数に制限を設けるのはなぜですか?私の現在のサーバー実装はzeromqを使用し、接続されているWebSocketクライアントごとにサブスクライバーソケットを開きます。明らかに、その単一のプロセスは、fdsの限界までクライアントを処理することしかできません。このトピックを調査すると、システムの制限を64k fdsまで上げる方法について多くの情報が見つかりましたが、システムのパフォーマンスにどのように影響するのか、最初は1k以下である理由については触れられていません。私の現在のアプローチは、独自のループ内のコルーチンと、すべてのクライアントとそのサブスクリプションチャネルのマップを使用して、すべてのクライアントにメッセージングをディスパッチすることです。
4 に答える
これは、ファイル記述子の値がファイル記述子テーブルへのインデックスであることが原因である可能性があります。したがって、可能なファイル記述子の数によってテーブルのサイズが決まります。平均的なユーザーは、RAMの半分が、必要のない何百万ものファイル記述子を処理できるファイル記述子テーブルによって使い果たされることを望んでいません。
パフォーマンスを向上させるために、開いているファイルテーブルを静的に割り当てる必要があるため、そのサイズを固定する必要があります。ファイル記述子はこのテーブルへのオフセットにすぎないため、すべてのエントリが連続している必要があります。テーブルのサイズを変更できますが、これには、プロセス内のすべてのスレッドを停止し、ファイルテーブルに新しいメモリブロックを割り当ててから、すべてのエントリを古いテーブルから新しいテーブルにコピーする必要があります。これは動的に実行したいことではありません。特に、実行している理由が古いテーブルがいっぱいであるためである場合はそうです。
潜在的なファイル記述子がたくさんある場合、速度が低下する特定の操作があります。stdin
1つの例は、「 、、stdout
およびを除くすべてのファイル記述子を閉じる」という操作stderr
です。これを行うための唯一の移植可能な*方法は、これら3つを除くすべての可能なファイル記述子を閉じようとすることです。ファイル記述子の数が開きます。
*:移植性を持たないことをいとわない場合は、調べてみてください/proc/self/fd
-しかし、それは重要なことではありません。
これは特に良い理由ではありませんが、理由です。もう1つの理由は、バグのあるプログラム(つまり、ファイル記述子を「リーク」するプログラム)がシステムリソースを過度に消費しないようにするためです。
UNIXシステムでは、プロセス作成fork()およびfork()/ exec()イディオムでは、それぞれを閉じようとするすべての潜在的なプロセスファイル記述子を反復処理する必要があります。通常、stdin、stdout、stderrなどのいくつかのファイル記述子のみがそのまま残ります。別の場所にリダイレクトされました。
これはプロセスを起動するためのUNIXAPIであるため、シェルスクリプト内で呼び出されるすべての非組み込みコマンドの実行を含め、新しいプロセスが作成されるたびに実行する必要があります。
考慮すべき他の要因は、一部のソフトウェアはsysconf(OPEN_MAX)
プロセスによって開かれる可能性のあるファイルの数を動的に決定するために使用する場合がありますが、多くのソフトウェアは依然としてCライブラリのデフォルトFD_SETSIZE
(通常は1024記述子)を使用するため、これを超えることはできません。管理上定義された上限に関係なく、多くのファイルが開きます。
Unixには、待機するファイルと準備ができているか例外状態にあるファイルを表すためにビットオフセットを使用するファイル記述子セットに基づくレガシー非同期I/Oメカニズムがあります。これらの記述子セットは、実行ループの前後で毎回セットアップしてクリアする必要があるため、何千ものファイルに対しては適切にスケーリングされません。多数の記述子を処理する際のパフォーマンスの欠点に対処するためにkqueue()
、*BSDやLinuxを含む主要なUNIXバリアントに新しい非標準APIが登場しました。epoll()
select()/poll()
長い間、非同期I /O用のPOSIXAPIであったため、多くのソフトウェアでまだ使用されていることに注意することが重要です。最新のPOSIX非同期IOアプローチは現在APIですが、またはAPIaio_*
と競合しない可能性があります。私はaioを怒りで使用したことはなく、複数のイベントを集約してパフォーマンスを向上させる方法で、ネイティブアプローチによって提供されるパフォーマンスとセマンティクスを確実に備えていません。* BSDのkqueue()は、イベント通知のための非常に優れたエッジトリガーセマンティクスを備えているため、アプリケーションに大きな構造変更を加えることなく、select()/ poll()を置き換えることができます。Linux epoll()は、* BSD kqueue()のリードに従い、Sun /Solarisevportsのリードに従って改良されています。kqueue()
epoll()
結果として、システム全体で許可されるオープンファイルの数を増やすと、使用しているAPIに基づいて記述子を使用できない場合でも、システム内のすべてのプロセスに時間とスペースの両方のオーバーヘッドが追加されます。許可される開いているファイルの数についても、システム全体の制限があります。FreeBSDでnginxを使用した100k〜200kの同時接続に関するこの古いが興味深いチューニングの概要は、オープン接続を維持するためのオーバーヘッドと、より広い範囲のシステムをカバーするが、10K接続をエベレスト山として「のみ」見るオーバーヘッドについての洞察を提供します。
おそらく、UNIXシステムプログラミングの最良のリファレンスは、Unix環境でのW.リチャードスティーブンスアドバンストプログラミングです。