ライブラリでのEINTRの適切な処理方法


10

EINTR図書館で推奨されるエチケットは何ですか?

現在、POSIX APIでいくつかのファイルシステムタスクを実行する関数を書いていますが、使用する多くの呼び出しはを返す可能性がありEINTRます。さらに、この関数は状況によってはブロックされる場合があります。(興味のある人のために、それはロック機構を実装しています。)

これをできるだけ一般的にするために、中断されたシステムコールを処理する適切な方法を知りたいと思います。

  • 私が読んだほとんどの情報源から、人々は通常、電話を再試行し、ビジネスを続けます。ただし、かなりの時間がかかる場合があるため、機能を中断する正当な理由がある場合があるため、ここでそれを行うのが正しいかどうかはわかりません。さらに、それはEINTR単にが関数に飲み込まれることを意味し、呼び出し元はそれが発生したことを示しません。

  • 私の現在の戦略はEINTR、発信者に受信して通知した場合、すぐに操作を中止することです。このようにして、呼び出し元は私の機能を再試行するかどうかを決定できますよね?(または、おそらく私の信号の理解に欠陥がありますか?)


1
あなたの図書館は何をしていますか?私はあなたの現在の戦略(操作を中止EINTRして呼び出し元に報告する)を承認する傾向がありますが、それはあなたのライブラリが何をしているかに依存するかもしれません...
Basile Starynkevitch '31

1
この場合、ロック操作を実行しています。
Rufflewind 2014

回答:


2

処理方法の決定は常にEINTRユーザーに任せ、必要に応じて操作簡単に再開できるようにします。

通常、これを行うための最良の方法は、ライブラリ関数から呼び出し元に戻ることですEINTRが、場合によっては、コールバックまたは他の実装の方が優れていることもあります。どちらの方法が他の要因に最適であるかは常に異なりますが、ユーザーが常に再試行して、履歴書。

つまり、ライブラリコードがを取得する前に部分的に成功するEINTR可能性がある場合、その部分的な成功についてユーザーが何を知る必要があるか、またはユーザーが失敗したところから操作を再開する必要があるかどうかを慎重に検討する必要があります。追加情報を返すか、適切な場所から再開するためのインターフェースを提供する必要がある場合があります。

これが、システムコールが次のようにreadなり、write今日では部分的な成功を返す理由です。

あなたは書こう"foo"と我々は成功したのいずれか何も書いていない、または"f"、あるいは"fo"あなたはどの知るために得ることはありません。楽しんで!あなたのシステムがそれらのいずれかの後に書き込み全体の再開を処理できることを願っています!

もちろん、場合によっては、まさにそのような状況を処理するシステムを作成する必要があります。たとえば、おそらく部分的な書き込みの後、常にファイルを再作成するか、ネットワーク接続を再度開くか、またはバイトを使用して「やり直し」を意味します-そのため、ライブラリが対象とするユースケースによって異なります。

ライブラリ関数がいくつかの操作を実行し、どの操作で失敗したかを知る方法がなく、それらの操作がすべて安全かつ効率的にべき等であるとは限らない場合、基本的に、堅牢にする必要があるコードでライブラリを使用できなくなります。

ライブラリ関数のすべてのステップ安全かつ効率的にべき等である場合、またはロックの取得のようにすべてがアトミックである場合は、EINTR発生したことで十分であることをユーザーに通知するだけです。

また、で再試行するとEINTRシグナル処理が中断する可能性があります。低レベルでは、シグナルハンドラーは安全に使用できる機能のセットが限られているため、多くの場合、シグナルハンドラーはブール値を設定してシグナルを受信したことを示し、コードが再開すると期待して戻ります。それがやっていたことから抜け出す。を取得EINTRし、ユーザーに制御を返す代わりに再試行する場合、コードがそれを行わない可能性があります。

何の後に行うことEINTR、プログラム全体の決定正しい答えは、プログラムがやって、どのようにプログラムが信号に応答するものとする、そして、それはプログラムの残りの部分への影響を持っているものを知らなくても知ることができません- 。

ユーザーが再開する必要があるかどうか、または再開する必要があるかどうかを知り必要に応じてユーザーを支援することは、ライブラリーの責任です。ライブラリーが何をしているかを知らない限り、正しい答えはわかりません。


4

Unixのシグナル

他のプロセスがプロセスシグナルを送信すると、プログラムは実行中の処理を停止します...

1)記述したハンドラーコードを実行します。シグナルが到着したときに、プログラムが何をしているのかわかりません。それが信号の考え方であり、完全に非同期にすることができます。

2)シグナルハンドラーが完了すると、通常は戻り値が返され、プログラムは中断したところから何も起こらなかったかのように続行します。

Richard Stevensに役立つ情報が見つかりました

以前のUNIXシステムの特徴は、プロセスが「遅い」システムコールでブロックされている間にプロセスがシグナルをキャッチした場合、システムコールが中断されたことです。システムコールがエラーを返し、errnoがEINTRに設定されました。これは、シグナルが発生してプロセスがそれをキャッチしたため、ブロックされたシステムコールをウェイクアップする必要がある何かが発生した可能性が高いという想定の下で行われました。

中断された読み取りと書き込みのPOSIX.1セマンティクスは、2001バージョンの標準で変更されました。以前のバージョンでは、部分的な量のデータを処理した読み取りと書き込みを処理する方法の選択肢が実装にありました。読み取りがデータを受信して​​アプリケーションのバッファーに転送したが、アプリケーションが要求したすべてのデータをまだ受信しておらず、その後中断された場合、オペレーティングシステムはerrnoをEINTRに設定してシステムコールに失敗するか、システムコールを成功させて戻ります。受信したデータの部分的な量。同様に、アプリケーションのバッファーにデータの一部を転送した後に書き込みが中断された場合、オペレーティングシステムはerrnoをEINTRに設定してシステムコールを失敗させるか、システムコールを成功させて、書き込まれたデータの一部を返します。歴史的に、System Vから派生した実装はシステムコールに失敗しますが、BSDから派生した実装は部分的に成功します。2001バージョンのPOSIX.1標準では、BSDスタイルのセマンティクスが必要です。

中断されたシステムコールの問題は、エラーの戻りを明示的に処理する必要があることです。典型的なコードシーケンス(読み取り操作を想定し、中断された場合でも読み取りを再開したいと想定)は次のようになります。

again:
    if ((n = read(fd, buf, BUFFSIZE)) < 0) {
        if (errno == EINTR)
            goto again;     /* just an interrupted system call */
        /* handle other errors */
    }

アプリケーションが中断されたシステムコールを処理する必要がないように、4.2BSDは特定の中断されたシステムコールの自動再起動を導入しました。


3
これは質問の答えにはなりません。それが図書館が問題に取り組むべき方法でした。特定の操作でEINTRを無視したい場合は、この呼び出しの再試行(goto again)でそれを行うことができます。しかし、ライブラリ開発者として、これが私のライブラリユーザーが望んでいるものかどうかはわかりません。
メッサ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.