Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5635181
  • 博文数量: 922
  • 博客积分: 19333
  • 博客等级: 上将
  • 技术积分: 11226
  • 用 户 组: 普通用户
  • 注册时间: 2007-03-27 14:33
文章分类

全部博文(922)

文章存档

2023年(1)

2020年(2)

2019年(1)

2017年(1)

2016年(3)

2015年(10)

2014年(17)

2013年(49)

2012年(291)

2011年(266)

2010年(95)

2009年(54)

2008年(132)

分类: LINUX

2012-01-22 20:22:27

++++++APUE读书笔记-10信号-05被中断的系统调用++++++
 
5、被中断的系统调用
================================================
 早期Unix系统的一个特性是当一个进程被阻塞在一个很慢的系统调用的时候捕捉到一个信号,这时候,这个系统调用就会被中断了。系统调用会返回一个错误,错误号码是EINTR,这个在发生信号并且进程捕捉到这个信号的时候就会发生,当进程被阻塞在系统调用的时候,这可以用来唤醒进程。
 这里,我们一定要区分系统调用和一个函数之间的区别。这是内核中的系统调用在捕获信号的时候被打断了。
 系统把系统调用区分为两类以支持这个特性:“慢”系统调用,以及其他的系统调用。慢系统调用是可以被永远阻塞的系统调用,比如说:
 a.如果特定文件(管道,终端驱动,网络设备)的数据不存在,可以导致调用者永久阻塞的读取操作。
 b.如果向相应的特定文件写数据不能被立即接受,可导致调用者永久阻塞的操作。
 c.打开特殊文件需要特殊条件导致的阻塞。(例如打开终端设备等待连接的modem应答的情况)
 d.pause函数(这个函数将调用进程睡眠,直到捕获到了一个信号)和wait函数。
 e.特定的ioctl操作。
 f.一些内部进程的通信函数。
 这些慢系统调用中一个例外是和磁盘相关的I/O操作。尽管对磁盘文件的读写操作可以临时阻塞一个调用者(磁盘驱动中会将请求排队,然后依次执行这些请求),除非出现硬件错误,I/O操作一般都会很快地返回,并且取消对调用者的阻塞。
 一个被打断的系统调用的情况就是当用户从终端处初始化了读取的操作,然后终端前的用户离开了很长时间,这样进程将被阻塞好久,直到系统挂掉。
 POSIX.1的2001标准版本改变了被打断的读/写操作的语义。早期的版本实现,处理只传输了一部分的读写操作的方式有不同的选择。如果读取操作将数据接收到应用程序的缓存,但是并没有将所有请求的数据接收就被打断了,那么操作系统可以让这次的read系统调用失败,然后设置errno为EINTR或者允许这次系统调用成功,然后返回被传输的那部分数据。类似地,如果write操作也是在传输了一部分数据的时候被打断了,那么操作系统也可以让write有类似地两种返回的情况。以前,继承自System V的系统会让系统调用失败,然而继承自BSD的系统,会让返回部分的数据。在2001版本的POSIX.1标准中,BSD类型的系统的语义被采用了。
 一个被打断的系统调用的问题就是我们需要显示地处理返回的错误状态,一个典型的代码序列如下(假设有一个读取操作并且我们就算在它被打断的时候也想要重新启动这个读取操作):
 again:
 if ((n = read(fd, buf, BUFFSIZE)) < 0)
 {
  if (errno == EINTR)
   goto again;     /* just an interrupted system call */
  /* handle other errors */
 }
 为了防止应用程序处理被打断的系统调用,4.2BSD对一些特定的被打断的系统调用引入了一个自动启动的机制。可以自动启动的系统调用有ioctl,read,readv,write,writev,wait,waitpid.前5个操作只有在操作一个比较慢的设备的时候,才会被信号打断;wait和waitpid在捕获信号的时候就会被打断。由于这个可能会导致应用程序的一些问题,有的应用程序并不是想要那个操作在被打断的时候重启动,4.3BSD允许进程在信号的地方来禁止这个特性。
 POSIX.1允许执行重新启动系统调用,但是这个不是需要的。Single UNIX Specification为sigaction定义了一个SA_RESTART标记作为XSI扩展,允许应用程序请求那个系统调用被重新启动。
 默认来说System V不会重新启动系统调用。而BSD如果被信号打断的时候,会重新启动他们。默认来说FreeBSD 5.2.1, Linux 2.4.22, 和 Mac OS X 10.3会重新启动被信号打断的系统调用。然而,Solaris9 会返回error为(EINTR)。
 4.2BSD引入自动启动系统调用这个特性的一个原因就是,有时候,我们不知道输入输出设备是一个慢速设备。如果我们写的程序可以被用来交互,那么它可能在读写一个慢速的设备,终端就是这个类型。如果我们在这个程序中捕获信号,并且系统不提供重新启动系统调用的能力,那么我们就得在每次读写的时候测试中断错误,或者返回并且重新进行读写操作。
 下面的表格中给出了各种系统实现的和signal相关的函数以及它们的含义。
+----------------------------------------------------------------------------------------------+
|           |                          | Signal handler  | Ability to  | Automatic restart of  |
| Functions |          System          |     remains     |    block    |  interrupted system   |
|           |                          |    installed    |   signals   |        calls?         |
|-----------+--------------------------+-----------------+-------------+-----------------------|
|           | ISO C, POSIX.1           |   unspecified   | unspecified |      unspecified      |
|           |--------------------------+-----------------+-------------+-----------------------|
|           | V7, SVR2, SVR3, SVR4,    |                 |             |         never         |
|           | Solaris                  |                 |             |                       |
|  signal   |--------------------------+-----------------+-------------+-----------------------|
|           | 4.2BSD                   |        •        |      •      |        always         |
|           |--------------------------+-----------------+-------------+-----------------------|
|           | 4.3BSD, 4.4BSD, FreeBSD, |        •        |      •      |        default        |
|           | Linux, Mac OS X          |                 |             |                       |
|-----------+--------------------------+-----------------+-------------+-----------------------|
|           | XSI                      |        •        |      •      |      unspecified      |
|  sigset   |--------------------------+-----------------+-------------+-----------------------|
|           | SVR3, SVR4, Linux,       |        •        |      •      |         never         |
|           | Solaris                  |                 |             |                       |
|-----------+--------------------------+-----------------+-------------+-----------------------|
|           | 4.2BSD                   |        •        |      •      |        always         |
|  sigvec   |--------------------------+-----------------+-------------+-----------------------|
|           | 4.3BSD, 4.4BSD, FreeBSD, |        •        |      •      |        default        |
|           | Mac OS X                 |                 |             |                       |
|-----------+--------------------------+-----------------+-------------+-----------------------|
|           | POSIX.1                  |        •        |      •      |      unspecified      |
|           |--------------------------+-----------------+-------------+-----------------------|
| sigaction | XSI, 4.4BSD, SVR4,       |                 |             |                       |
|           | FreeBSD, Mac OS X,       |        •        |      •      |       optional        |
|           | Linux, Solaris           |                 |             |                       |
+----------------------------------------------------------------------------------------------+
 我们没有讨论旧的sigset和sigvec函数。他们的作用已经sigaction替换了;包含它们只是为了完整。相反,有些实现提倡使用signal函数作为简化的sigaction接口。
 需要注意的是其他的UNIX系统,可能会和表中的有所不同。例如,在SunOS 4.1.2的sigaction默认会重新启动被中断的系统调用,这和上面表中列出的是不一样的。
 在本章第14节中,我们提供了自己的signal函数,这个函数尝试自动重新启动被打断的系统调用(除了SIGALRM信号)。后面我们会提供另外的函数signal_intr这个函数就从来不会尝试重新启动。
 我们会在后面涉及到select和poll函数的时候讨论更多关于被打断的系统调用。
参考:
 
 
阅读(846) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

vaqeteart2013-10-16 16:29:22

产生信号时,进程被挂起,处理完信号,进程恢复执行。信号处理函数中,还是可以调用被挂起进程的上下文(如变量等)。处理函数是软中断而非在多线程方式并发处理。

vaqeteart2013-10-16 15:28:22

mark