web-dev-qa-db-ja.com

* non-blockingness *のlibuv実装はどのように正確に機能しますか?

だから、私は libuv がCライブラリに関する限りかなり小さいライブラリであることを発見しました(FFmpegと比較してください)。私は過去6時間をかけてソースコードを読み、より深いレベルでのイベントループの感触をつかみました。しかし、それでも「非ブロッキング性」が実装されている場所がわかりません。コードベースでイベント割り込み信号などが呼び出されている場所。

Node.jsを8年以上使用しているので、非同期のノンブロッキングイベントループを使用する方法に慣れていますが、実際に実装を調べたことはありません。

私の質問は2つあります。

  1. どこに正確に「ループ」がlibuv内で発生していますか?
  2. non-blockingおよびasyncを作成するループの各反復における主要なステップは何ですか。

したがって、Hello Worldの例から始めます。必要なのはこれだけです:

_#include <stdio.h>
#include <stdlib.h>
#include <uv.h>

int main() {
  uv_loop_t *loop = malloc(sizeof(uv_loop_t));
  uv_loop_init(loop); // initialize datastructures.
  uv_run(loop, UV_RUN_DEFAULT); // infinite loop as long as queue is full?
  uv_loop_close(loop);
  free(loop);
  return 0;
}
_

私が探求している主要な関数は_uv_run_です。 _uv_loop_init_関数は、基本的にデータ構造を初期化するので、あまりファンシーではないと思います。しかし、本当の魔法は_uv_run_、どこかで発生するようです。 libuvリポジトリからのコードスニペットの高レベルセットは このGistで であり、_uv_run_関数が呼び出すものを示しています。

基本的に、これは次のように要約されます。

_while (NOT_STOPPED) {
  uv__update_time(loop)
  uv__run_timers(loop)
  uv__run_pending(loop)
  uv__run_idle(loop)
  uv__run_prepare(loop)
  uv__io_poll(loop, timeout)
  uv__run_check(loop)
  uv__run_closing_handles(loop)
  // ... cleanup
}
_

これらの機能は要点にあります。

  • _uv__run_timers_:タイマーコールバックを実行しますか? for (;;) {でループします。
  • _uv__run_pending_:通常のコールバックを実行しますか? while (!QUEUE_EMPTY(&pq)) {でキューをループします。
  • _uv__run_idle_:ソースコードなし
  • _uv__run_prepare_:ソースコードなし
  • _uv__io_poll_:ioポーリングはしますか? (これが何を意味するのかはっきりとは言えません)。 2つのループがあります:while (!QUEUE_EMPTY(&loop->watcher_queue)) {、およびfor (;;) {

これで完了です。そして、実行する「作業」がないため、プログラムが存在します。

だから私はこのすべての掘り下げの後に私の質問の最初の部分に答えたと思います、そしてループは特にこれらの3つの機能にあります:

  1. _uv__run_timers_
  2. _uv__run_pending_
  3. _uv__io_poll_

しかし、kqueueまたはマルチスレッドを使用して何も実装せず、ファイル記述子をほとんど処理しなかったため、コードを完全には理解していません。これはおそらく、これを学ぶための道に沿って他の人も助けるでしょう。

したがって、質問の2番目の部分は非ブロッキング性を実装するこれら3つの関数の主要なステップは何ですか?です。これがすべてのループが存在する場所であると仮定します。

Cのエキスパートではない場合、for (;;) {はイベントループを「ブロック」しますか?または、それが無期限に実行され、OSシステムイベントなどからコードの他の部分にジャンプすることはできますか?

したがって、_uv__io_poll_は、その無限ループでpoll(...)を呼び出します。非ブロッキングではないと思いますが、それは正しいですか?それは主にそれを行うすべてのようです。

_kqueue.c_を見ると、_uv__io_poll_もあるので、poll実装がフォールバックであり、Macでkqueueが使用されていると思いますが、これは非ブロッキングですか?

それですか?それは単に_uv__io_poll_でループし、キューに追加できる各反復で、キューに何かがある限り、実行されますか?それがどのように非ブロッキングで非同期であるかはまだわかりません。

これに似た1つのアウトラインで、非同期および非ブロッキングの方法、およびコードのどの部分を確認することができますか?基本的に、私はlibuvの「フリープロセッサのアイドル状態」がどこにあるかを確認したいと思います。最初の_uv_run_の呼び出しでプロセッサが解放される場所はどこですか?それが無料の場合、イベントハンドラーのように、どのように再起動されますか? (マウスからのブラウザイベントハンドラのように、割り込み)。割り込みを探しているようですが、割り込みが表示されません。

CでMVPイベントループを実装したいので、これを要求しますが、ノンブロッキングが実際にどのように実装されているかを理解できません。ゴムが道路に出会うところ。

6
Lance Pollard

最初に覚えておかなければならないのは、APIを使用してlibuvのキューに作業を追加する必要があることです。 libuvをロードしてメインループを開始し、I/Oをいくつかコード化して非同期I/Oを取得することはできません。

Libuvによって維持されるキューは、ループによって管理されます。 uv__run_timersの無限ループは、実際には無限ではありません。最初のチェックでは、最も早く期限切れになるタイマーが存在することを確認し(おそらく、リストが空の場合、これはNULLです)、そうでない場合は、ループを中断して関数が戻ります。現在の(すぐに期限切れになる)タイマーが期限切れになっていない場合、次のチェックはループを中断します。これらの条件のどちらでもループが中断されない場合、コードは続行します。コードはタイマーを再起動し、タイムアウトハンドラーを呼び出し、さらにループして、さらにタイマーをチェックします。ほとんどの場合、このコードが実行されると、ループが中断されて終了し、他のループを実行できるようになります。

このすべてを非ブロッキングにするのは、libuvのガイドラインとAPIに準拠した呼び出し元/ユーザーです。つまり、作業をキューに追加し、libuvがそれらのキューで作業を実行できるようにします。処理集中型の作業は、これらのループや他の作業の実行をブロックする可能性があるため、作業をチャンクに分割することが重要です。

0
Shon