被中断的系统调用

被中断的系统调用

早期UNIX系统的一个特性是如果进程被一个“慢”系统调用阻塞的同时捕获了一个信号(这个系统调用已经被中断)。系统调用返回一个错误并且设置errno的值为EINTR。假定这发生在信号发生并且进程捕获到它时。这有可能会唤醒被阻塞的系统调用。(这里必须区分系统调用和函数的不同,系统调用是在内核中,当信号被捕捉时才会被中断。)

为了支持这一功能,系统调用分为两类:“慢”系统调用和其它。慢系统调用是能被永远阻塞的。包含以下几种:

  • 如果数据没有提供某种文件类型(pipes,终端设备和网络设备),读的时候就能被永久阻塞。
  • 如果数据不能被同一文件类型立即接收,写的时候就能被永久阻塞。
  • 当打开文件时,阻塞会持续到需要一些条件发生在某些文件类型上(像是打开一个终端设备,而该终端设备要等待绑定的调制解调器的拨号应答)。
  • pause函数(用于休眠一个进程,直到信号被捕获)和wait函数。
  • 某种ioctl操作
  • 一些进程间通讯函数

“慢”系统调用中重要的异常都是和磁盘I/O有关的。虽然磁盘文件的读或写能临时阻塞调用者(当磁盘驱动程序对请求进行排队之后响应这些请求),除非发生硬件错误,否则I/O操作一直快速地返回并解锁调用者。

有一种情况可以用被中断的系统调用处理。例如:当一个进程创建了从终端设备上读的操作,并且用户离开了终端很长的时间。在这种情况下,进程可能被阻塞几个小时或几天并且保留进程,除非系统关闭。

被中断的系统调用有一个问题,那就是我们现在必须显示的处理返回的错误。典型的代码序列可能是(假设一个读操作并且假设我们想重新开始读,即使它已经被中断):

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

为了防止应用程序必须处理被中断的系统调用,BSD4.2引入了自动重启某些被中断的系统调用。能被自动重启的系统调用有:ioctl, read, readv, write, writev, waitwaitpid。如同我们提到过的前五个函数如果操作一个慢设备,那么会被一个信号中断。waitwaitpid当信号被捕获时总是被中断。因为这会引起一些问题,像一些应用不希望操作被中断后重新启动,BSD4.3允许进程在每个信号上屏蔽这个功能。

BSD4.2引入自动重启系统调用这个功能的原因之一是:有时我们并不知道输入或输出设备是一个慢速设备。如果我们写了一个用于交互的程序,只要终端进入了这个分类,那么它可能读或写一个慢速设备。如果在这个程序运行期间我们捕获了一个信号,并且当前系统不提供自动重启这个功能,那么我们必须为返回的中断错误测试每一个读写,并重新读写(reissue the read or write)。

Figure 10.3汇总了信号函数和各个版本的用法(their semantics provided by the various implementations)

Figure 10.3. Features provided by various signal implementations

Functions

System

Signal handler remains installed

Ability to block signals

Automatic restart of interrupted system calls?

signal

ISO C, POSIX.1

unspecified

unspecified

unspecified

V7, SVR2, SVR3, SVR4, Solaris

never

4.2BSD

always

4.3BSD, 4.4BSD, FreeBSD, Linux, Mac OS X

default

sigset

XSI

unspecified

SVR3, SVR4, Linux, Solaris

never

sigvec

4.2BSD

always

4.3BSD, 4.4BSD, FreeBSD, Mac OS X

default

sigaction

POSIX.1

unspecified

XSI, 4.4BSD, SVR4, FreeBSD, Mac OS X, Linux, Solaris

optional

不同发行商的实现可能上表中的值也有所不同。例如:sigaction函数,在SunOS 4.1.2重启被中断的系统调用是默认,Figure10.3列出了不同平台的区别。

在Figure 10.18我们提供了我们自己的signal函数,它尝试自动重启被中断的系统调用(不同于SIGALRM信号)。在Figure 10.19我们提供了另一个函数,signal_intr,它尝试从不重启。

发表回复