Interrupted System
Calls
A
characteristic of earlier UNIX systems is 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.
Here,
we have to differentiate between a system call and a function. It is a system
call within the kernel that is interrupted when a signal is caught.
To
support this feature, the system calls are divided into two categories: the
"slow" system calls and all the others. The slow system calls are
those that can block forever. Included in this category are
- Reads that can block the caller forever if data isn't present with certain file types (pipes, terminal devices, and network devices)
- Writes that can block the caller forever if the data can't be accepted immediately by these same file types
- Opens that block until some condition occurs on certain file types (such as an open of a terminal device that waits until an attached modem answers the phone)
- The pause function (which by definition puts the calling process to sleep until a signal is caught) and the wait function
- Certain ioctl operations
- Some of the interprocess communication functions.
The
notable exception to these slow system calls is anything related to disk I/O.
Although a read or a write of a disk file can block the caller temporarily
(while the disk driver queues the request and then the request is executed),
unless a hardware error occurs, the I/O operation always returns and unblocks
the caller quickly.
One
condition that is handled by interrupted system calls, for example, is when a
process initiates a read from a terminal device and the user at the terminal
walks away from the terminal for an extended period. In this example, the
process could be blocked for hours or days and would remain so unless the
system was taken down.
POSIX.1
semantics for interrupted reads and writes changed with the 2001 version of the standard. Earlier versions gave
implementations a choice for how to deal with reads and writes that have processed partial amounts of data. If read has received and transferred data to an application's buffer, but has
not yet received all that the application requested and is then interrupted,
the operating system could either fail the system call with errno set to EINTR
or allow the system call to succeed, returning
the partial amount of data received. Similarly, if write is interrupted after transferring some of the data in an application's
buffer, the operation system could either fail the system call with errno set to EINTR
or allow the system call to succeed, returning
the partial amount of data written. Historically, implementations derived from
System V fail the system call, whereas BSD-derived implementations return
partial success. With the 2001 version of the POSIX.1 standard, the BSD-style
semantics are required.
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
cal
/*
* handle other errors
*/
}
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.
POSIX.1
allows an implementation to restart system calls, but it is not required. The
Single UNIX Specification defines the SA_RESTART flag as an XSI
extension to sigaction
to allow applications to request that interrupted
system calls be restarted.
System
V has never restarted system calls by default. BSD, on the other hand, restarts
them if interrupted by signals. By default, FreeBSD 5.2.1, Linux 2.4.22, and
Mac OS X 10.3 restart system calls interrupted by signals. The default on
Solaris 9, however, is to return an error (EINTR) instead.
One of
the reasons 4.2BSD introduced the automatic restart feature is that sometimes
we don't know that the input or output device is a slow device. If the program
we write can be used interactively, then it might be reading or writing a slow
device, since terminals fall into this category. If we catch signals in this
program, and if the system doesn't provide the restart capability, then we have
to test every read or write for the interrupted error return and reissue the
read or write.
No comments:
Post a Comment