10.5 Interrupted System Calls

Posted U201013687

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了10.5 Interrupted System Calls相关的知识,希望对你有一定的参考价值。

早期UNIX系统的一个特点就是:当进程被阻塞再一个“slow”的系统调用中的时候如果捕获到一个信号,系统调用就会被中断,然后系统调用返回一个错误,其中errno被设置为EINTR.这可以实现使用一些事件的发生来唤醒被阻塞的系统调用。
为了支持这一特性,系统调用被分为两类:slow的系统调用,以及其他系统调用,所谓的slow系统调用是指可能永远阻塞的系统调用,包括如下系统调用:

  • 读操作可能会永远阻塞系统调用,如果对于某些文件类型来说数据还没有准备好的话(if data isn’t present with certain types),文件类型包括管道,终端以及网络设备。
  • 写操作对于某些文件类型来说也可能永远阻塞,如果数据不能被这些文件类型立即接受的话。
  • 打开某些特定的文件类型也可能一直阻塞调用进程,直到某些条件出现为止。比如说终端设备的打开需要一直等待,直到一个进行连接的modem应答请求。(until an attached modem answers the phone).
  • pause函数以及wait函数,该函数的定义就是将调用进程放到sleep状态,直到捕获到一个信号为止。
  • 特定的ioctl操作
  • 一些进程间通信函数(具体见第15章)。

这些slow系统调用的一个明显的例外是任何与磁盘IO相关的操作,虽然读写磁盘文件会短暂地阻塞调用进程(磁盘会将请求排队,然后挨个执行请求),除非硬件错误出现,否则IO操作总是很快返回,并不会阻塞调用进程。

One condition that is handled by interrupted system calls, for example, is when aprocess 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对于interrupted reads以及writes的定义在2001版本的标准中进行了修改,早期的实现对于reads或者writes已经处理了部分数据的情况给出了两种不同的选择。如果read已经接受并传送到应用程序的缓冲区中一部分数据,但是还没有达到应用请求的数量的时候被中断掉了,其结果可能是系统调用执行失败,并将errno设置为EINTR,或者允许系统调用成功,并返回已经介绍到的部分数据,类似地,如果writes函数在传送了部分数据以后被中断,操作系统要么将本次系统调用设置为失败,并将errno设置为EINTR,或者允许系统调用成功,并返回已经写出成功的部分数量。在历史上,继承自System V的系统会在上述情况下将系统调用设置为失败,而继承自BSD的实现会返回部分成功。在2001版本的POSIX.1标准中,选择了BSD类型的定义。

现在我们对于被中断的系统调用需要进行的处理是:必须显示地处理错误的返回。典型的处理代码如下所示(假设是一个读操作并且假设我们在操作被中断以后想要重启读操作):

  1. again:
  2. if( (n = read(fd, buf, BUFFERSIZE) < 0)
  3. {
  4. if(errno == EINTR)
  5. {
  6. goto again; /*just an interrupted system call*/
  7. }
  8. /*handle other errors*/
  9. }

为了避免应用程序去处理被中断的系统调用,4.2BSD引入了某些被中断系统调用的自动重启,这些在被中断以后可以被自动重启的系统调用包括:ioctl,read,readv,write,writev,wait,and waitpid,正如我们提到的,前面五个系统调用在操作慢速设备的时候可能会被中断掉,系统调用wait以及waitpid在信号出现的时候总是被中断,因为这样做对于一些并不想要在操作被中断的时候被重启的应用有问题,因此4.3BSD允许进程不使能某一信号的这一特性。

POSIX.1要求仅仅当正在中断的信号受到SA_RESTART函数的影响的时候需要重启系统调用,正如我们在10.14节中将看到的,该标志用于sigaction的参数,从而允许应用程序请求被中断的系统调用重启。

历史上,在使用函数signal来建立信号处理函数的时候,对于被中断的系统调用将会如何处理与实现有关,System V默认情况下从来不会重启系统调用,与之相对应的是,BSD在系统调用被信号中断的时候将会进行重启操作,在FreeBSD8.0,Linux3.2.0以及Mac OS X10.6.8上,当信号处理函数被使用signal安装的时候,被中断的系统调用都将被重启,在Solaris 10上的默认行为是返回一个错误(EINTR),通过使用我们在图10.18中实现的signal函数,我们可以避免处理这些实现差异。

4.2BSD引入自动重启被中断的系统调用的其中一个原因是:程序设计人员并不清楚输入或者输出设备是否是慢速设备,如果我们写的应用程序可能被交互使用,那么它就可能对一个慢速设备进行读写操作,因为终端设备属于这一类型,在这样一个程序中捕获到一个信号,而系统又不提供自动重启的操作的话,我们就必须检查每一次的read以及write函数是否出现被中断的错误,然后再次对read以及write进行处理。

图10.3总结了signal函数以及不同实现提供的定义:

Functions System Signal handler remains installed Ability to block signals Automatic restart of interrupted system calls?
signal ISO C,POSIX.1 unspecified unspecified unspecified
signal V7,SVR2,SVR3 never
signal SVR4,Solaris never
signal 4.2BSD * * always
signal 4.3BSD,4.4BSD,FreeBSD,Linux,Mac OS X * * default
sigaction POSIX.1,4.4BSD,SVR4,FreeBSD,Linux,Mac OS X,Solaris * * optional

Figure 10.3 不同signal实现提供的特征

需要注意的是,来自不同开发商的UNIX系统对于上表中的情况各有不同,比如说,SunOS 4.1.2下的sigaction函数会默认重启一个被中断的系统调用。
在图10.18中,我们提供了我们自己的signal函数,该函数将会尝试重启被中断的系统调用(除了SIGALRM信号之外),在图10.19中,我们提供了另外一个函数signal_intr,该函数从不会尝试执行重启操作。

在14.4中将会对被中断的系统调用进行更细致的讨论。(关于函数select以及poll)





以上是关于10.5 Interrupted System Calls的主要内容,如果未能解决你的问题,请参考以下文章

Java 学习笔记之 线程interrupted方法

谜题84:被粗暴地中断

工具类:Math

10.5 simulated match

在 Mac 10.5 上拆解 C++ 名称

interrupted()和isInterrupted()比较