看看 man 2 read,发现:
EINTR The call was interrupted by a signal before any data was read; see signal(7).
EINTR 错误在 read, recv, recvfrom,recvmsg,write,send,sendto,sendmsg 等函数被信号打断时会发生,所以我就打算模拟一下这种情况:
/* myread() 函数的一部分,在 while (1) 大循环里 */
if ((n = read(fd, buf, nbytes)) < 0) {
if (errno == EINTR) {
perror("EINTR");
n = 0;
} else {
perror("read error");
return -1;
}
} else {
break;
}
/* main() 函数中 */
signal(SIGALRM, sig_alrm);
alarm(1);
myread(fd, buf, MAX);
/* sig_alrm() 函数 */
void sig_alrm(ing signo)
{
return;
}
fd 是一个 tcp socket,sig_alrm 函数什么也不做,因为目的只是打断而已,但是在运行时,程序完全没有被打断,然后我又换成 recvfrom 和 recv,结果还是不会被打断!!这就很神奇了,我开始找原因,一通乱翻:
recvfrom not returning -1 after signal - stackoverflow
在这里找到了解答,然后再看看这个 SA_RESTART 到底是怎么回事,在 man 2 sigaction:
SA_RESTART
Provide behavior compatible with BSD signal semantics by making certain system calls restartable across signals. This flag is meaningful only when establishing a signal handler. See signal(7) for a discussion of system call restarting.
再看看 man 7 signal:
If a signal handler is invoked while a system call or library function call is blocked, then either :
- the call is automatically restarted after the signal handler returns; or
- the call fails with the error EINTR.
man 7 signal 还提到,recv, recvfrom, recvmsg 会 always fail with the error EINTR when interrupted by a signal handler, regardless of the use of SA_RESTART。但是!!我经过实验,发现只要 sigaction 设置了 SA_RESTART 标志,这几个函数就完全不会被打断了。
大概,就像这样:
struct sigaction sa;
sa.flags = 0;
sa.sa_handler = sig_alrm;
sigaction(SIGALRM, &sa, NULL);
alarm(1);
recv(fd, buf, MAX, 0);
if (errno == EINTR)
perror("recv error");
结果是:
recv error: Interrupted system call
而
sa.flags = SA_RESTART;
就不会被打断。
总结:signal 很复杂啊,文档也是会骗人的。