Interrupted system call

interruptsignalssystem-calls

I am reading APUE and the Interrupted System Calls chapter confuses me.

I would like to write down my understanding based on the book, please correct me.

  1. A characteristic of earlier UNIX systems was that if a process caught
    a signal while the process was blocked in a ‘‘slow’’ system call, the
    system call was interrupted. The system call returned an error and
    errno was set to EINTR. This was done under the assumption that since
    a signal occurred and the process caught it, there is a good chance
    that something has happened that should wake up the blocked system
    call.

    So it's saying that the earlier UNIX systems has a feature: if my program uses a system call, it would be interrupted/stopped, if at any time the program catches a signal. (Does default handler also count as a catch?)

    For example, if I have a read system call, which reads 10GB data, when it's reading, I send any one of signals(e.g.kill -SIGUSR1 pid), then read would fail and return.


  1. To prevent applications from having to handle interrupted system
    calls, 4.2BSD introduced the automatic restarting of certain
    interrupted system calls. The system calls that were automatically
    restarted are ioctl, read, readv, write, writev, wait, and waitpid. As
    we’ve mentioned, the first five of these functions are interrupted by
    a signal only if they are operating on a slow device; wait and waitpid
    are always interrupted when a signal is caught. Since this caused a
    problem for some applications that didn’t want the operation restarted
    if it was interrupted, 4.3BSD allowed the process to disable this
    feature on a per-signal basis.

So before automatic restarting was introduced, I had to handle interrupted system call on my own. I need write code like:

The problem with interrupted system calls is that we now have to
handle the error return explicitly. The typical code sequence
(assuming a read operation and assuming that we want to restart the
read even if it’s interrupted) would be:

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

But nowadays I don't have to write this kind of code, beacause of the automatic restarting mechanism.


So if I my understanding are all correct, what/why should I care about interrupted system call now..? It seems the system/OS handles it automatically.

Best Answer

Interruption of a system call by a signal handler occurs only in the case of various blocking system calls, and happens when the system call is interrupted by a signal handler that was explicitly established by the programmer.

Furthermore, in the case where a blocking system call is interrupted by a signal handler, automatic system call restarting is an optional feature. You elect to automatically restart system calls by specifying the SA_RESTART flag when establishing the signal handler. As stated in (for example) the Linux signal(7) manual page:

   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)). 

As hinted by the last sentence quoted above, even when you elect to use this feature, it does not work for all system calls, and the set of system calls for which it does work varies across UNIX implementations. The Linux signal(7) manual page notes a number of system calls that are automatically restarted when using the SA_RESTART flag, but also goes on to note various system calls that are never restarted, even if you specify that flag when establishing a handler, including:

   * "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).

For these system calls, manual restarting using a loop of the form described in APUE is essential, something like:

while ((ret = some_syscall(...)) == -1 && errno == EINTR)
    continue;
if (ret == -1)
    /* Handle error */ ;
Related Question