在超时调用读取时出现缺陷

时间:2014-11-18 09:00:34

标签: c unix signals system-calls

我是系统编程的新手,我遇到了这个程序。

#include "apue.h"
static void sig_alrm(int);
int main(void)
{  
     int n;
     char line[MAXLINE];
     if (signal(SIGALRM, sig_alrm) == SIG_ERR)
     err_sys("signal(SIGALRM) error");
     alarm(10);
     if ((n = read(STDIN_FILENO, line, MAXLINE)) < 0)
         err_sys("read error");
     alarm(0);
     write(STDOUT_FILENO, line, n);
     exit(0);
}
static void sig_alrm(int signo)
{
        /* nothing to do, just return to interrupt the read */
}

此程序旨在为read()系统调用提供超时。如果read()需要超过30秒,那么将生成一个信号。现在,

从教科书中引用(它列出了程序中的2个缺陷,其中之一是)

  

如果系统调用自动重启,则SIGALRM不会中断读取   信号处理程序返回在这种情况下,超时不执行任何操作。

我无法理解这句话的意思。有人可以解释一下吗

感谢。

1 个答案:

答案 0 :(得分:2)

以下注意事项对Linux&lt; = 2.6.11有效,但我相信这些概念也适用于最近的内核。

如果某个进程在系统调用中被阻止,则会将其置于TASK_INTERRUPTIBLETASK_UNINTERRUPTIBLE状态的等待队列中。在前一种情况下,在接收到信号后,内核将其重新置于TASK_RUNNING状态,将进程添加到runqueue并将信号添加到挂起信号列表中。

安排进程后,它会继续执行系统调用。由于系统调用未完成,可能的返回码为:

  • EINTR
  • ERESTARTNOHAND
  • ERESTART_RESTARTBLOCK
  • ERESTARTSYS
  • ERESTARTNOINTR

在系统调用的退出路径上,检查挂起的信号。在这种情况下,调用您的SIG_ALRM处理程序,然后内核立即检查系统调用的返回代码。通常,可能会发生以下情况:

  • 返回代码为EINTR:用户模式进程已恢复,read的返回值将完全符合-EINTR
  • 返回代码为ERESTARTNOINTR:系统调用已重新执行
  • 返回代码为ERESTARTSYS:如果设置了SA_RESTART标志,则重新执行系统调用,否则将-EINTR返回给用户空间。
  • 返回代码为ERESTARTNOHANDERESTART_RESTARTBLOCK:恢复用户模式进程并返回-EINTR。

重新执行系统调用意味着进程EIP减少2,因此强制它再次执行int 0x80

在这种特定情况下,如果进程在STDIN的读取系统调用上被阻止,并且从SIG_ALRM收到man 7 signal,则信号处理程序中断系统调用和库函数< /强>:

If a blocked call to one of the following interfaces is
interrupted by a signal handler, then the call will be
automatically restarted after the signal handler returns 
if the SA_RESTART  flag  was used; otherwise the call will 
fail with the error EINTR.

* read(2),  readv(2),  write(2),  writev(2), and ioctl(2) 
calls on "slow" devices.  A "slow" device is one where the 
I/O call may block for an indefinite time, for example, a
terminal, pipe, or socket.  (A disk is not a slow device 
according to this definition.)  If an I/O call on a slow 
device has already transferred some data by the time 
it is interrupted by a signal handler, then the call 
will return a success status (normally, the number of bytes
transferred).

这是ERESTARTSYS返回代码的行为,即只有在设置SA_RESTART时捕获信号时才重新执行系统调用,否则 它将返回EINTR(如果尚未传输数据)。

所以你书中的第一句话

If system calls are automatically restarted [...]

表示

If, upon receiving a signal, SA_RESTART is set

如果是,系统调用重新启动,它将再次阻止,警报不会产生任何影响。

<强>参考文献:

  • 了解Linux内核第3版,第10章(系统调用),第11章(信号)
  • man 7 signal