Linuxネットワークの高度なプログラミング-libcocoroutineイベント駆動型およびスケジューリング

1.コルティンの「ブロッキング」とスレッドの「非ブロッキング」

スレッド内のすべてのcoroutineは、前の章のコードで、基本的にシリアルに実行されますhttps//blog.csdn.net/qq_44065088/article/details/109272505

Producer coroutine関数でpoll関数が呼び出され1秒間待機しco_cond_timedwait関数も呼び出されてConsumerのプロデューサー信号待機していることがわかります。コルーチンの観点からは、これらの待機は同期(同期)およびブロッキング(ブロッキング)のように見えますが、基になるスレッドの観点からは、非ブロッキング(非ブロッキング)であることに注意してください。 例:スレッドのプロデューサー-コンシューマーモデルから、pthread_cond_waitもスレッドをブロックします。スレッドの観点からは、スレッドはブロックされているように見えますが、カーネルの観点からは、それ自体はブロックされません。カーネルは、他のスレッドの実行を急いでスケジュールしてください。同じ理由:コロチンは同じです。コルーチン自体の場合、ブロッキングが実現されますが、以下のスレッドの場合、他のコルーチン機能の実行でビジー状態になる可能性があります。 

この例では、 Consumer coroutineがco_cond_timedwait 関数を 呼び出して「ブロック」すると、スレッドは Producerの スケジューリング 再開した可能性があり、その逆も同様です。では、このスレッドは、コルーチンの「スケジューリング」をどこで担当しているのでしょうか。 これは、コルーチン自体を実行しているスレッドです[ したがって、このスレッドが実際にブロックされている場合、結果として、コルーチン全体のフレームワークがブロックされている場所で機能しなくなります ]。

 

カーネルのブロッキングを回避するには、カーネルが提供する非ブロッキングIOメカニズムに依存し、fcntlを使用してソケットファイル記述子をnonBlockに設定する必要があります。もちろん、libcoも非常に配慮されています。これは、このnonBlockプロセスをカプセル化し、「同期ブロッキング」のふりをします。 (co_cond_timedwait()と同じ)。実際、go languageはこれを行いますが、libcoの偽装はより徹底的で欺瞞的であるため、目が私たちを欺くことがよくあります。Libcoはdlsymメカニズムを介してさまざまなネットワークIO関連のシステム呼び出しをフックし、ユーザーが「同期」できるようにします。""メソッドは、読み取り、書き込み、接続などの関数を直接使用するため、co_enable_hook_sys()がプロデューサーコンシューマーcoroutine関数で呼び出されていることがわかります [フックシステム呼び出し関数は、呼び出し後にオンになり、実装にはファイル記述子をnoblockに設定します。そうしないと、スレッドブロッキングに分類されます]

プロセスについて簡単に説明します。ソケット接続がある場合は、最初に非ブロッキングに設定してから、coroutineを開始してリンクを処理し、coroutineがreadを呼び出して、この新しい接続からの読み取りを試みます。データ、この時点で呼び出された読み取りはフックオフされているため、目で騙されないでください。4つ機能します

  • 1.この読み取り(偽のMonkey King)は、現在のコルルーチンをタイマーに登録して、将来の読み取りタイムアウトを処理します。
  • 2. epoll_ctl()を呼び出して、現在の実行環境のepollインスタンスに自分自身を登録します。登録プロセスの2つのステップでは、コールバック関数を指定する必要があります。この関数は、将来、現在のコルーチンを「ウェイクアップ」するために使用されます。
  • 3. co_yield_env関数を呼び出して、CPUを放棄します
  • 4.メインのcoroutineepoll_wait()は、読み取り操作のファイル記述子が読み取り可能であることを認識している場合、元の読み取りcoroutineによって登録されたコールバックを実行してウェイクアップします。
仕事の儀式は 目覚めた後 、元のglibc 呼び出す 際に、 置き換えられフック内にあり真のread()システム呼び出しです。このとき、ファイル記述子I / Oの準備できているのが正常なepoll_waitの場合はデータを読み取り、時間外の場合は-1を返します。つまり、外部ユーザーの目には、このread()はブロッキングシステム呼び出しとほぼ同じ動作を示します。

 

 

 2.メインコルチンとコルチンの「スケジューリング」

 メインコルーチン[メインスレッドがco_createなどの関数を呼び出すのと同じ理由で、プロセスが低下します]

コンシューマーまたはプロデューサーがブロックされると、CPUメインコルチンに譲ります、この時点でメインコルチンは何をしていますか?メインのcoroutineはco_eventloop()関数でビジーです。このco_eventloop(イベントループ)の「スケジューラー」の中心であるこのco_eventloop()は、実際にはepoll / kqueueイベントループ(前述)に基づいているため、通常のスケジューリングイベント駆動型は密接に関連しています。 、したがって、libcoはネットワークライブラリであるため、それほど日常的なライブラリではありませんバックグラウンドサーバープログラムでは、すべてのロジックはネットワークI / Oを中心に展開されlibcoのような設計には独自の合理性があります。

みんなのために絵を描きます

降伏がブロックされたためにゴルーチン1がCPUをメインスレッドに渡すと、イベントによって駆動され、どのゴルーチンにIOイベントがあるかが判別され、CPUの実行能力が応答するゴルーチンに再開されます。実行後、CPUをyieldでメインコルチンに戻します(このプロセスが繰り返されます

 

3.stCoEpoll_t構造

stCoRoutineEnv_t 構造を 分析する場合、言及されていないstCoEpoll_tタイプのpEpollポインターメンバー もあり ます。stCoRoutineEnv_tのメンバーとして、この構造はグローバルリソースでもあり、同じスレッド上のすべてのcoroutineによって共有されます。また、stCoEpoll_tがあること、ネーミングからも分かると関連しているファイルディスクリプタのイベントループ。次に、その内部フィールドを確認します。

 

struct stCoEpoll_t {  
       int iEpollFd; // epollのどのルートファイル記述子が明らかにあるか
       static const int _EPOLL_SIZE = 1024 * 10; // epoll_wait の3番目のパラメーターとして[ epoll_waitによって返される準備完了イベントの最大数 ]
       struct stTimeout_t * pTimeout; //  タイプstTimeout_tの構造ポインター。構造は実際には⼀ タイムホイール(タイミングホイール)タイマーで 、奇妙な名前です
       struct stTimeoutItemLink_t * pstTimeoutList; //  stTimeoutItemLink_tタイプの構造へのポインター。ポインタは実際にはリンクされたリストヘッドです。 アイテムの一時ストレージタイムアウトイベントの リスト
       struct stTimeoutItemLink_t * pstActiveList; //  stTimeoutItemLink_tタイプの構造へのポインター。また、リンクされたリストを指します。このリストは、 epoll_wait getreadyイベント timertimeoutイベントを保存するために使用され ます
 
       co_epoll_res * result; // epoll_wait()の 2番目のパラメーター 、つまり1つの epoll_wait から取得した 結果セットのカプセル化
};

 

タイマーはイベント駆動型モデルのネットワークフレームワークに不可欠な機能であることがわかっています。ネットワークI / Oのタイムアウト、タイミング待機(pollまたはtimedwait を含むタイミングタスクはすべてこれに依存します。

一般的に、タイマー機能を使用する場合は、まずタイマーイベント(タイマーイベント)をタイマーに登録します。タイマーイベントを登録する場合は、今後このイベントのトリガー時間を指定する必要があります。トリガー時間に達すると、タイマー通知を受け取ります。

ネットワークフレームワークのタイマーは、2つの部分で構成されていると見なすことができます。最初の部分は 登録されたタイマーイベント 格納するためのデータ構造 であり、2番目の部分は タイミング通知メカニズム です。
タイマーの最初の部分: 登録されたタイマーイベント 、通常は赤黒ツリー[libeventのタイマーが使用するバイナリヒープ/キュー]、nginxなどを 保存し ます。 もう1つの一般的なデータ構造は、libcoが使用するタイムホイールです。この構造 もちろん、リンクリストを直接使用してそれを実現することもできますが、時間の複雑さが比較的高く、タイミングタスクが多い場合、フレームワークのパフォーマンスのボトルネックになりやすくなります。
タイマーの2番目の部分: 高精度のタイミング(マイクロ秒レベルまで正確)通知メカニズム。通常、getitimer / setitimerなどのインターフェイスが使用されます。信号を処理する必要があり、これはより面倒です。 epollは、タイミングのためにlibco内で直接使用されます。 唯一の違いは、タイムホイールがタイマーイベントの保存に使用されることです。

 

おすすめ

転載: blog.csdn.net/qq_44065088/article/details/109282354