web-dev-qa-db-ja.com

中断されたシステムコールとは何ですか?

私は[〜#〜] apue [〜#〜]を読んでおり、Interrupted System Callsの章で混乱しています。

その本に基づいて私の理解を書き留めたいのですが、訂正してください。

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

    つまり、以前 UNIXシステムには機能があるということです。プログラムがシステムコールを使用している場合、プログラムがいつでもシグナルをキャッチすると、中断または停止されます。 (デフォルトのハンドラーもキャッチとしてカウントされますか?)

    たとえば、10GBのデータを読み取るreadシステムコールがある場合、読み取り時に、シグナルのいずれか1つを送信します(例:kill -SIGUSR1 pid)の場合、readは失敗して戻ります。


  1. アプリケーションが中断されたシステムコールを処理する必要がないように、4.2BSDは、特定の中断されたシステムコールの自動再起動を導入しました。自動的に再起動されたシステムコールは、ioctlreadreadvwritewritevwait、およびwaitpid。前述したように、これらの機能の最初の5つは、遅いデバイスで動作している場合にのみ信号によって中断されます。 waitwaitpidは、シグナルがキャッチされると常に中断されます。これにより、中断された場合に操作の再開を望まない一部のアプリケーションで問題が発生したため、4.3BSDでは、プロセスがシグナルごとにこの機能を無効にできるようにしました。

したがって、自動再起動が導入される前は、中断されたシステムコールを自分で処理する必要がありました。私は次のようなコードを書く必要があります:

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

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

しかし、最近は自動再起動メカニズムのため、この種のコードを記述する必要はありません。


したがって、私の理解がすべて正しい場合、今すぐシステムコールの中断を気にする必要があるのはなぜですか?システム/ OSが自動的に処理するようです。

2
Rick

シグナルハンドラーによるシステムコールの中断は、さまざまなブロッキングシステムコールの場合にのみ発生し、プログラマーによって明示的に確立されたシグナルハンドラーによってシステムコールが中断されたときに発生します。

さらに、ブロッキングシステムコールがシグナルハンドラーによって中断された場合、システムコールの自動再起動はオプション機能です。シグナルハンドラーを確立するときに_SA_RESTART_フラグを指定して、システムコールを自動的に再起動することを選択します。 (例えば)Linux signal(7) マニュアルページで述べたように:

_   If  a  signal  handler  is  invoked while a system call or library
   function call is blocked, then either:

   * the call is automatically restarted  after  the  signal  handler
     returns; or

   * the call fails with the error EINTR.

   Which  of  these two behaviors occurs depends on the interface and
   whether or not  the  signal  handler  was  established  using  the
   SA_RESTART  flag (see sigaction(2)). 
_

上記の最後の文で示唆されているように、この機能を使用することを選択した場合でも、すべてのシステムコールで機能するわけではなく、機能するシステムコールのセットはUNIXの実装によって異なります。 Linux signal(7)のマニュアルページには、_SA_RESTART_フラグを使用すると自動的に再起動されるシステムコールの数が記載されていますが、指定しても、再起動されないさまざまなシステムコールが記載されています。ハンドラーを確立するときのフラグ。

_   * "Input" socket interfaces, when a timeout (SO_RCVTIMEO) has been
     set  on  the  socket  using  setsockopt(2):  accept(2), recv(2),
     recvfrom(2), recvmmsg(2) (also with  a  non-NULL  timeout  argu‐
     ment), and recvmsg(2).

   * "Output"  socket  interfaces,  when  a timeout (SO_RCVTIMEO) has
     been set on the socket using setsockopt(2): connect(2), send(2),
     sendto(2), and sendmsg(2).

   * File   descriptor   multiplexing   interfaces:    epoll_wait(2),
     epoll_pwait(2), poll(2), ppoll(2), select(2), and pselect(2).

   * System  V  IPC  interfaces:  msgrcv(2), msgsnd(2), semop(2), and
     semtimedop(2).
_

これらのシステムコールでは、APUEで説明されている形式のループを使用して手動で再起動することが不可欠です。

_while ((ret = some_syscall(...)) == -1 && errno == EINTR)
    continue;
if (ret == -1)
    /* Handle error */ ;
_
3
mtk

[私はそのAPUEのことは読んでいませんが、引用しているものは見栄えが良くありません]

私のプログラムがシステムコールを使用している場合、いつでもプログラムがシグナルをキャッチすると、割り込み/停止されます。

システムコールはありません。 someシステムコールのみが割り込み可能です。

(デフォルトのハンドラーもキャッチとしてカウントされますか?)

番号。

たとえば、10GBのデータを読み取る読み取りシステムコールがある場合、読み取り時に、シグナルのいずれか1つ(たとえば、kill -SIGUSR1 pid)を送信すると、読み取りが失敗して戻ります。

10 GBのread()は、読み取りが可能になる前に中断された場合にのみEINTRで戻ります1バイトでも;それ以外の場合は、すでに読み取ったデータの量を返します(短い読み取り=成功、errnoは関係ありません)。

[これはリンクされただましで説明されていません]

したがって、私の理解がすべて正しい場合、今すぐシステムコールの中断を気にする必要があるのはなぜですか?システム/ OSが自動的に処理するようです。

シグナルを受け取ったときにsomethingを実行したい場合があり、シグナルハンドラからはあまり実行できないためです。 malloc()またはstdio(printf()を含む)を使用するものは問題外です。したがって、プログラムのメインループで割り込みを処理する必要があり、それを実行できるようにするには、何らかの方法でブロックread()を解除する必要があります(read()は、poll()がfdを返した後でもブロックできる読む準備ができています)。

[これはでしたもリンクされたデュープで説明されています]

3
Uncle Billy