看看 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 很复杂啊,文档也是会骗人的。