Linuxデバイスドライバーを作成するためのブロッキングI/O関数について学び、ERESTARTSYS
の使用方法を知りたいです。以下を検討してください。
グローバル変数:
_wait_queue_head_t my_wait_q_head;
int read_avail = 0;
_
device_init():
init_waitqueue_head(&my_wait_q_head);
device_read():
_printk("I'm inside driver read!\n");
wait_event_interruptible(&my_wait_q_head, read_avail != 0);
printk("I'm awaken!\n");
_
device_write():
_read_avail = 1;
wake_up_interruptible(&my_wait_q_head);
_
ユーザースペースからread()
を呼び出すと、期待どおりにwrite()
を呼び出すまでコマンドプロンプトがハングします。 printk
メッセージもdmesg
に表示されます。ただし、次のように書かれたドライバがいくつかあります。
Device_read()の別のバージョン:
_printk("I'm inside driver read!\n");
if(wait_event_interruptible(&my_wait_q_head, read_avail != 0))
{return -ERESTARTSYS;}
printk("I'm awaken!\n");
_
ユーザー空間で同じメソッドを使用してdevice_read()
の2番目のバージョンをテストしました。結果はまったく同じなので、ERESTARTSYSの用途は何ですか?
p/s:これについては、Linux Device Driverという本を読んだのですが、理解できません。誰かが例を挙げて詳しく説明できますか?:
その呼び出しを通過すると、何かが起きてしまいましたが、何が起きているのかわかりません。 1つの可能性は、プロセスがシグナルを受信したことです。 wait_event_interruptible呼び出しを含むifステートメントは、このケースをチェックします。このステートメントは、シグナルへの適切で予期される反応を保証します。これは、プロセスをウェイクアップする原因となった可能性があります(割り込み可能なスリープ状態だったため)。シグナルが到着し、プロセスによってブロックされていない場合、適切な動作は、カーネルの上位層にイベントを処理させることです。このために、ドライバーは呼び出し元に-ERESTARTSYSを返します。この値は、仮想ファイルシステム(VFS)レイヤーによって内部的に使用され、システムコールを再起動するか、ユーザー空間に-EINTRを返します。同じタイプのチェックを使用して、すべての読み取りおよび書き込み実装の信号処理を処理します。
-ERESTARTSYSは、再起動可能なシステムコールの概念に関連しています。再起動可能なシステムコールは、割り込みが発生したときにカーネルによって透過的に再実行できるシステムコールです。
たとえば、システムコールでスリープしているユーザー空間プロセスは、シグナルを受け取ってハンドラーを実行し、ハンドラーが戻ると、カーネルに戻って元のシステムコールでスリープし続けるように見えます。
POSIXの使用 sigaction
APIのSA_RESTART
フラグ。プロセスは、シグナルに関連付けられた再起動動作を調整できます。
Linuxカーネルでは、システムコールのコンテキストでブロックしているドライバーまたは他のモジュールが、タスクがシグナルのために起こされたことを検出すると、-EINTRを返すことがあります。ただし、-EINTRはユーザー空間にバブリングし、システムコールはerrnoをEINTRに設定して-1を返します。
代わりに-ERESTARTSYSを返す場合は、システムコールが再起動可能であることを意味します。 ERESTARTSYSコードは、必ずしもユーザー空間に表示されるとは限りません。これは、-1の戻りに変換され、errnoがEINTRに設定されます(その後、明らかにユーザー空間で表示されます)、またはシステムコールの再起動動作に変換されます。つまり、同じ引数を使用してsyscallが再度呼び出されます(ユーザー空間プロセスの一部に対してアクションはありません。カーネルは、特別な再起動ブロックに情報を隠しておくことでこれを行います。
前の段落の「同じ引数」の明らかな問題に注意してください。一部のシステムコールは、べき等ではないため、同じパラメーターで再起動できません。たとえば、5.3秒間、nanosleepのようなスリープコールがあるとします。 5秒後に中断されます。単純に再起動すると、さらに5.3秒間スリープします。残りの0.3秒間だけスリープするには、再起動された呼び出しに新しいパラメーターを渡す必要があります。つまり、再起動ブロックの内容を変更します。これを行う方法があります。タスクの再起動ブロックにさまざまな引数を入れ、-ERESTART_RESTARTBLOCK戻り値を使用します。
2番目の質問に対処するには:違いは何ですか?戻り値をチェックして-ERESTARTSYSを返さずに、単に読み取りルーチンを作成しないのはなぜですか?まあ、それはウェイクアップが信号による場合は正しくないからです!シグナルが到着するたびに、読み取りで0バイトの読み取りを返しますか?これは、データの終わりとしてユーザー空間によって誤って解釈される可能性があります。この種の問題は、信号を使用しないテストケースでは発生しません。