12

のような多くのシステムコールはclose( fd )、シグナルによって中断される可能性があります。この場合、通常-1は が返され、errno設定されEINTRます。

問題は、何をするのが正しいかということです。言ってください、私はまだこれfdを閉じたいと思っています。

私が思いつくことができるのは:

while( close( fd ) == -1 )
  if( errno != EINTR ) {
    ReportError();
    break;
  }

この状況を処理するためのより良い/よりエレガントな/標準的な方法を提案できる人はいますか?

更新: mux が気づいたRESTARTように、シグナル ハンドラをインストールするときに SA_ フラグを使用できます。POSIXだけでなく、すべてのシステムで再起動可能であることが保証されている機能を誰か教えてもらえますLinuxか?

4

3 に答える 3

9

SA_RESTART一部のシステム コールは再起動可能です。これは、シグナル ハンドラのインストール時にフラグが使用されている場合、中断された場合にカーネルがコールを再起動することを意味します。signal (7)のマニュアル ページには次のように記載されています。

次のインターフェイスのいずれかへのブロックされた呼び出しがシグナル ハンドラーによって中断された場合、SA_RESTART フラグが使用されていれば、シグナル ハンドラーが戻った後に呼び出しが自動的に再開されます。そうしないと、呼び出しはエラー EINTR で失敗します。

close()再起動可能かどうかは言及されていませんが、これらは次のとおりです。

read(2)、readv(2)、write(2)、writev(2)、ioctl(2)、open(2)、wait(2)、wait3(2)、wait4(2)、waitid(2)、および waitpid、accept(2)、connect(2)、recv(2)、recvfrom(2)、recvmsg(2)、send(2)、sendto(2)、および sendmsg(2) flock(2) および fcntl( 2) mq_receive(3)、mq_timedreceive(3)、mq_send(3)、および mq_timedsend(3) sem_wait(3) および sem_timedwait(3) futex(2)

これらの詳細、特に再開不可能な呼び出しのリストは、Linux 固有のものであることに注意してください。

どのシステムコールが再起動可能かについての関連する質問を投稿しました。それがどこかで POSIX によって指定されている場合、それは POSIX によって指定されていますが、オプションであるため、OS の再起動不可能な呼び出しのリストを確認する必要があります。再起動可能。これは私の質問です: Linux システム コールが再起動可能かどうかを知るにはどうすればよいですか?

更新:クローズは、再起動できない特殊なケースであり、Linux で再試行すべきではありません。詳細については、この回答を参照してください: https://stackoverflow.com/a/14431867/1157444

于 2012-11-13T07:06:00.950 に答える
2

記録のために: 基本的にすべての UNIX で、EINTR を返す場合はclose() 再試行してはなりません。またはの場合のように、EINTR 再試行ループを close に配置しないでください。詳細については、次のページを参照してください: http://austingroupbugs.net/view.php?id=529 Linux、Solaris、BSD などでは、再試行は正しくありません。HP-UX は、これを必要とする唯一の一般的な (!) システムです。waitpid()read()close()

EINTR は、 andなどに対してread()とは非常に異なるものを意味します。ほとんどの呼び出しでは、EINTR で再試行します。これは、ブロックする何かを実行するように要求したためです。中断された場合は、それが起こらなかったことを意味するため、再試行します。の場合、あなたが要求したアクションは、エントリを fd テーブルから削除することでした。これはエラーなしで瞬時に行われ、何が返されても常に発生します。TCP linger)、I/O が完了するまで待ってから戻ることができます。close が EINTR を返す場合は、待機するように要求したが、待機できなかったことを意味します。ただし、fd はまだ閉じられていました。あなたはそれを待つ機会を失っただけです。select()waitpid()close()close()close()close()

結論: シグナルを受信できないことがわかっている場合を除きclose()、待機を使用するのは非常に愚かなことです。アプリケーション レベルの ACK (TCP) または fsync (ファイル I/O) を使用して、fd を閉じる前に書き込みが完了していることを確認します。

[*] 注意点があります: プロセスの別のスレッドが同じ fd のブロッキング syscall 内にある場合は、... 場合によって異なります。

于 2013-03-14T15:12:36.777 に答える
2

より短いコードを求めていると仮定すると、次のようなものを試すことができます。

while (((rc = close (fd)) == -1) && (errno == EINTR));
if (rc == -1)
    complainBitterly (errno);

より短いコードに加えて、より読みやすいコードを求めていると仮定すると、関数を作成するだけです:

int closeWithRetry (int fd);

そこに読み取り可能なコードを配置します。次に、それがどれくらいの長さであるかは問題ではありません。それを呼び出す場所はまだワンライナーですが、関数本体自体を非常に読みやすくすることができます。

int closeWithRetry (int fd) {
    // Initial close attempt.

    int rc = close (fd);

    // As long as you failed with EINTR, keep trying.
    // Possibly with a limit (count or time-based).

    while ((rc == -1) && (errno == EINTR))
        rc = close (fd);

    // Once either success or non-retry failure, return error code.

    return rc;
}
于 2012-11-13T06:43:57.367 に答える