web-dev-qa-db-ja.com

Linuxブロッキングと非ブロッキングシリアル読み取り

私は このコード をLinuxのシリアルから読み取るために持っていますが、シリアルポートの読み取りにおけるブロッキングと非ブロッキングの違いと、どの状況でどちらが優れているかわかりませんか?

25

あなたが言及するコードは、IMOのコーディングとコメントが不十分です。そのコードは、 ターミナルモードの適切な設定 および POSIXオペレーティングシステム用シリアルプログラミングガイド で説明されているように、移植性に関するPOSIXプラクティスに準拠していません。そのコードは、非標準(別名raw)モードを使用することを言及しておらず、「ブロッキング」および「ノンブロッキング」の用語を再利用して[〜#〜] vmin [〜#〜]および[〜#〜] vtime [〜#〜]属性。

(そのコードの作者は、それがPOSIX標準に先立っており、したがって準拠していないことを報告しています。それは理解できますが、移植できない古いコードの使用を投稿して提唱する(つまり、代替状況で期待どおりに機能する) )は疑わしい。)

「ブロッキング」対「ノンブロッキング」読み取りの従来の定義は、読み取り呼び出しがプログラムに戻るとき(および次のステートメントで実行を再開するとき)と、プログラムの読み取りバッファーにデータが格納されるかどうかに基づいています。 O_NONBLOCKまたはO_NDELAYオプションでシリアルポートを開いて非ブロッキングが要求されない限り、ブロッキング読み取りがデフォルトモードです。

標準モード
ブロッキング 標準読み取り シリアルポートの呼び出しの場合、テキストの行(別名レコード)が常に提供されたバッファーに返されます(エラーが発生しない限り)。読み取り呼び出しは、行終了文字が受信されて処理されるまでブロックされます(つまり、プログラムの実行を一時停止します)。

シリアルポートの非ブロッキング標準読み取り呼び出しは、常に「即時」を返します。読み取りによってデータが返される場合と返されない場合があります。
少なくとも1行のテキストが受信され、システムバッファーに格納されている場合(前回の読み取り呼び出し以降)、最も古い行がシステムバッファーから削除され、プログラムのバッファーにコピーされます。戻りコードはデータ長を示します。
(前の読み取り呼び出し以降)行終了文字が受信および処理されていない場合、使用可能なテキストの(完全な)行はありません。 read()はEAGAINエラーを返します(つまり、-1の戻りコードとerrnoセットEAGAINへ)。その後、プログラムは何らかの計算を実行したり、別のデバイスからI/Oを要求したり、遅延/スリープしたりできます。任意の遅延の後、またはpoll()またはselect()による通知によって、プログラムはread()

読み取りにブロッキング標準モードを使用するプログラム例は、 this answer に含まれています。

非標準モード
シリアルポートが非標準モードに設定されている場合、termiosc_cc配列要素[〜#〜] vmin [〜#〜]および[〜#〜] vtime [〜#〜] 「ブロッキング」を制御するために使用する必要がありますが、これにはデフォルトのブロッキングモードでポートを開く必要があります。つまり、O_NONBLOCKオープンオプションを指定しないでください。そうでない場合、O_NONBLOCKはVMINおよびVTIMEの指定よりも優先され、read()errnoを設定しますEAGAINに戻り、使用可能なデータがない場合は、すぐに0ではなく-1を返します。 (これは最近のLinux 3.xカーネルで見られる動作です。古い2.6.xカーネルは異なる動作をする可能性があります。)

Termiosのマニュアルページには、(c_cc配列インデックス)[〜#〜] vmin [〜#〜]「非標準読み取りの最小文字数」、および(c_cc配列インデックス)[〜#〜] vtime [〜#〜] "非標準読み取りのデシ秒単位のタイムアウト"
[〜#〜] vmin [〜#〜]は、予想される典型的なメッセージまたはデータグラムの長さおよび/または最小値に対応するために、プログラムによって調整する必要がありますread()ごとに取得および処理するデータのサイズ。
[〜#〜] vtime [〜#〜]は、予想されるシリアルデータの典型的なバースト性または到着率に対応するために、プログラムによって調整する必要があります。または、データまたはデータを待機する最大時間。

[〜#〜] vmin [〜#〜]および[〜#〜] vtime [〜#〜]値は相互作用します読み取りを返すタイミングの基準を決定します。それらの正確な意味は、どれが非ゼロかによって異なります。 4つの可能なケースがあります。
このWebページ は次のように説明しています:

  • VMIN = 0およびVTIME = 0

    これは完全に非ブロック読み取りです-呼び出しは、ドライバーの入力キューから直接直接満たされます。データが利用可能な場合、呼び出し元のバッファに最大nバイト転送され、返されます。それ以外の場合、「データなし」を示すためにゼロがすぐに返されます。これはシリアルポートの「ポーリング」であり、ほとんどの場合悪い考えであることに注意してください。繰り返し実行すると、膨大な量のプロセッサ時間が消費される可能性があり、非常に非効率的です。あなたが本当に何をしているのかを本当に知っていない限り、このモードを使用しないでください。

  • VMIN = 0およびVTIME> 0

    これは純粋な時限読み取りです。入力キューでデータが使用可能な場合、最大nバイトまで呼び出し元のバッファーに転送され、すぐに呼び出し元に返されます。それ以外の場合、ドライバーは、データが到着するまで、またはコールの開始からVTIME 10が期限切れになるまでブロックします。タイマーがデータなしで期限切れになると、ゼロが返されます。この読み取り呼び出しを満たすには1バイトで十分ですが、入力キューでさらに使用できる場合は、呼び出し元に返されます。これは全体的なタイマーであり、キャラクタ間タイマーではないことに注意してください。

  • VMIN> 0およびVTIME> 0

    Read()は、VMIN文字が呼び出し側のバッファーに転送されたとき、または文字間でVTIMEの10分の1が経過したときに満たされます。このタイマーは最初の文字が到着するまで開始されないため、シリアル回線がアイドル状態の場合、この呼び出しは無期限にブロックする可能性があります。これは最も一般的な動作モードであり、VTIMEは全体的なタイムアウトではなく、文字間タイムアウトと見なされます。この呼び出しは、読み込まれたゼロバイトを決して返すべきではありません。

(私の経験では、VMIN>0 and VTIME>0モードは、宣伝どおりに機能しません。タイマーは非常に短い間隔であり、1/10秒よりはるかに短いようです。私はそれがARM 2.6で、Linux 3.13でx86で動作するのを見たことがない。高速ボーレート(115200)、VMIN = 1およびVTIME = 1で、read()が時々戻る10バイト以上ですが、多くの場合、VTIME値に関係なく、数バイトの部分的な読み取りにすぎません。おそらく、この破損が望ましい/望ましいでしょうか?最新の高速ボーレートでは、最小0.1秒のメッセージ分離は単純に長すぎます(実用的ではありません) )

  • VMIN> 0およびVTIME = 0

    これは、少なくともVMIN文字が呼び出し元のバッファーに転送された場合にのみ満たされるカウント読み取りです。タイミングコンポーネントは含まれません。この読み取りは、ドライバーの入力キュー(呼び出しがすぐに戻る可能性がある)から、または新しいデータの到着を待つことで満たすことができます。この点で、呼び出しは無期限にブロックされる可能性があります。 nbytesがVMINより小さい場合、これは未定義の動作であると考えています。

あなたが言及するコードは、「非ブロック化」モードをVMIN = 0およびVTIME = 5として構成します。これにより、非ブロッキングの標準的な読み取りのようにread()がすぐに返されることはありません。そのコードでは、read()は常に少なくとも0.5秒待機してから戻る必要があります。 「ノンブロッキング」の従来の定義では、呼び出し元のプログラムはsyscall中にプリエンプトされず、すぐに(ほぼ)制御を取り戻します。 (非条件読み取り)即時リターン(非標準読み取りの場合)を取得するには、VMIN = 0およびVTIME = 0を設定します。

57
sawdust