IO多重化(select / poll / epol)、およびgolangIOでgoroutineとIO多重化を使用して実装された「非同期」IOモデル

転載元:https//zhuanlan.zhihu.com/p/344581947

個人的なバックアップの場合のみ、閲覧用の元のテキストを参照してください

 

目次

ioモデル

IOのブロック

ノンブロッキングIO

IO多重化(選択/ポーリング/エポールを含む)

select / poll / epollの違い

epollの紹介

非同期IO

Golang非同期IO実装のアイデア


 

Golangを使用すると、パフォーマンスの問題を心配することなく、TCP接続ごとにコルーチンを簡単に作成できます。これは、Goが内部でIO多重化と組み合わせたゴルーチンを使用して「非同期」IOモデルを実装しているため、開発者は不要になります。最下層であり、必要に応じて上位層のビジネスロジックのみを記述する必要があります。この非同期IOはどのように実現されますか?以下では、Linuxシステムを分析します。

Unix / Linuxシステムでは、すべてがファイルです。各TCP接続はソケットハンドルに対応します。このハンドルはファイルと見なすこともできます。ソケットでのデータの送受信は、ファイルの読み取りと書き込みに相当するため、ソケットハンドルです。 、通常、ファイル記述子fdを表すためにも使用されます。/ proc / PID / fd /と入力して、プロセスが占有しているfdを表示できます。

システムカーネルは、ソケットハンドルごとに読み取り(受信)バッファと書き込み(送信)バッファを割り当てます。データの送信は、このfdに対応する書き込みバッファにデータを書き込むことであり、データを受信することは、読み取りバッファでデータを読み取ることです。プログラムが書き込みまたは送信を呼び出す場合、データが送信されることを意味するのではなく、データを書き込みバッファにコピーするだけです。適切なタイミング(一定量に蓄積)になると、データは宛先に送信されます。

Golangランタイムは、fdの準備ができているかどうかを頻繁にチェックする必要があります。厳密に言えば、実際には非同期ではなく、非ブロッキングIOの再利用です。

ioモデル

IOのブロック

プログラムがバッファ内のデータを読み取りたい場合、バッファには必ずしもデータがないため、システムコールが発生し、データが読み取られるのを待つことしかできません。読み取るデータがない場合は、プロセスがブロックされます。 。これはIOをブロックしています。複数のクライアントにサービスを提供する必要がある場合は、スレッド化されたアプローチを使用できます。各ソケットハンドルはスレッドを使用してサービスを提供するため、特定のスレッドがブロックされます。これによりプロセスの輻輳を解決できますが、データの待機にかなりの量のCPUリソースが浪費されます。同時に、スレッドを使用してfdを提供することは、処理するfdが多い場合、リソースの浪費になります。別のリソースオーバーヘッド。

 

 

ノンブロッキングIO

これに対応するのはノンブロッキングIOです。プログラムがデータを読み取りたい場合、バッファが存在しない場合は直接ユーザープログラムに戻りますが、ユーザープログラムはデータの準備ができるまで頻繁にチェックする必要があります。これにより、CPUが空になります。

 

 

IO多重化(選択/ポーリング/エポールを含む)

含まれるもの:select / poll / epoll

そして、IO多重化は異なります。彼はスレッドを使用して複数のfdを管理します。複数のfdをIO多重化関数に追加できます。関数が呼び出されるたびに、チェックするfdを渡して、FDの準備ができている場合は、直接返します。レディfdを実行してから、スレッド処理を開始するか、レディfdを順次処理します。これにより、1つのスレッドで複数のfdタスクを管理できるようになり、比較的効率的です。一般的なIO多重化機能には、select、poll、およびepollが含まれます。selectとpollの最大の欠点は、各呼び出しが監視対象のすべてのfdコレクションを渡す必要があり、カーネルがこの着信fdコレクションをトラバースすることです。同時実行量が多い場合、ユーザーモードとカーネル間のデータコピーモードとカーネルのポーリングfdは、システムリソースの波を浪費します(ここでの選択とポーリングを拡張しないでください)。

 

select / poll / epollの違い

I / O多重化は、プロセスが複数のファイル記述子を監視できるメカニズムです。記述子の準備ができたら(読み取りまたは書き込みの準備ができたら)、対応する読み取りおよび書き込み操作を実行するようにプログラムに通知できます。ただし、select、poll、およびepollの性質は、読み取りおよび書き込みイベントの準備ができた後にすべてのスレッドが読み取りおよび書き込みを行う必要があるため、同期I / Oのカテゴリにあります(I / O多重化自体は同期IOです)。 、および読み取りと書き込みのプロセスがブロックされます。非同期I / Oの実現は、スレッド自体が読み取りと書き込みをブロックすることなく、システムがカーネル空間からユーザー空間にデータをコピーする責任を負い、カーネルを完成させる準備ができていることです。

(1)selectおよびpollの実装では、デバイスの準備が整うまで、すべてのfdコレクションをそれ自体で継続的にポーリングする必要があります。その間、デバイスはスリープとウェイクアップを複数回繰り返すことがあります。実際、epollはepoll_waitを呼び出して、準備完了リストを継続的にポーリングする必要があります。この期間中、スリープとウェイクアップが交互に行われる場合があります。ただし、デバイスの準備ができたときにコールバック関数を呼び出し、準備完了fdを準備完了リストに入れます。 epoll_waitプロセスでウェイクアップしてスリープ状態になります。スリープして交互にする必要がありますが、selectとpollは、「アウェイク」のときにfdセット全体をトラバースする必要があり、epollが「アウェイク」の場合は、準備完了リストが空かどうかを判断するだけで済み、多くの節約になります。 CPU時間。これは、コールバックメカニズムによってもたらされるパフォーマンスの向上です。

(2)選択し、各呼び出しをポーリングしてfdコレクションをユーザーモードからカーネルモードにコピーし、デバイスの待機キューに電流を1回ハングしますが、epollは1回コピーするだけで、電流を待機キューにハングします。1回だけハングします( epoll_waitの先頭で、ここでの待機キューはデバイス待機キューではなく、epollによって内部的に定義された待機キューであることに注意してください。これにより、オーバーヘッドを大幅に節約することもできます。

epollの紹介

次に、epollシステムコールを紹介します

selectやpollと比較して、epollはより柔軟で効率的であり、ユーザーに3つのシステムコール関数を提供します。Golangの最下層は、これら3つのシステムコールをgoroutineと組み合わせることによって完成する「非同期」IOです。

//用于创建并返回一个epfd句柄,后续关于fd的添加删除等操作都依据这个句柄。
int epoll_create(int size);
//用于向epfd添加,删除,修改要监听的fd。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
//传入创建返回的epfd句柄,以及超时时间,返回就绪的fd句柄。
int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
  • epoll_createを呼び出すと、カーネル内にeventpollオブジェクトが作成されます。このオブジェクトは、fdコレクションとして簡単に理解できるepitemコレクションを維持します。
  • epoll_ctl関数の呼び出しは、fdをエピテムにカプセル化してeventpollオブジェクトに結合し、このエピテムにコールバック関数を追加してカーネルに登録するために使用されます。このコールバック関数は、このfdの状態が変化したときにトリガーされ、エピテムが追加されます。 eventpoll準備リストrdlistに。
  • 対応するデータが到着すると、割り込み応答プログラムがトリガーされ、データがfdのソケットバッファにコピーされ、fdバッファの状態が変化し、コールバック関数がfdに対応するエピテムをrdlistreadyキューに追加します。
  • epoll_waitを呼び出す場合、トラバースする必要はありませんが、準備ができたrdlistキューが返されます。rdlistキューが空の場合、待機はブロックされるか、タイムアウト時間が到着します。

一般的な動作原理を図に示します

 

 

非同期IO

ユーザープログラムがfdデータを読み取りたい場合、システムコールはカーネルに直接通知し、他の処理に戻ります。カーネルがデータを準備した後、ユーザープログラムに通知し、ユーザープログラムはfdでイベントを処理します。

 

 

Golang非同期IO実装のアイデア

コルーチンのリソース占有率は非常に小さく、コルーチンにはブロック、準備完了、実行中などのさまざまな状態があることは誰もが知っています。コルーチンを使用すると、リソースの問題を気にせずに1つのfdを提供できます。fdを監視するイベントは、実行時に渡されて管理され、fdに依存するコルーチンのスケジューリングとイベントが実現されます。コルーチンがfdデータを読み取る必要があるがデータがない場合は、コルーチンをパークし(Gwaitingに変更)、他のコルーチンを実行するようにスケジュールします。

コルーチンスケジューリングを実行するときは、fdの準備ができているかどうかを確認します。準備ができている場合、スケジューラーはコルーチンfdに、パークが存在することを処理(Grunnableに変更して実行キューに追加)できることを通知し、コルーチンはfdを処理します。データにより、CPUの空の消費を削減するだけでなく、メッセージの通知を実現し、ユーザーレベルから非同期IOモデルを実現しました。

 

 

Golangnetpollの一般的な考え方はこれです

おすすめ

転載: blog.csdn.net/chushoufengli/article/details/115231156