环境:类似 RedHat 的发行版,2.6.39 内核,glibc 2.12。
我完全希望如果在 accept() 进行时传递了信号,则 accept 应该失败,留下 errno==EINTR。但是,我的不这样做,我想知道为什么。下面是示例程序和 strace 输出。
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#include <errno.h>
#include <arpa/inet.h>
#include <string.h>
static void sigh(int);
int main(int argc, char ** argv) {
int s;
struct sockaddr_in sin;
if ((s = socket(AF_INET, SOCK_STREAM, 0))<0) {
perror("socket");
return 1;
}
memset(&sin, 0, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET;
if (bind(s, (struct sockaddr*)&sin, sizeof(struct sockaddr_in))) {
perror("bind");
return 1;
}
if (listen(s, 5)) {
perror("listen");
}
signal(SIGQUIT, sigh);
while (1) {
socklen_t sl = sizeof(struct sockaddr_in);
int rc = accept(s, (struct sockaddr*)&sin, &sl);
if (rc<0) {
if (errno == EINTR) {
printf("accept restarted\n");
continue;
}
perror("accept");
return 1;
}
printf("accepted fd %d\n", rc);
close(rc);
}
}
void sigh(int s) {
signal(s, sigh);
unsigned char p[100];
int i = 0;
while (s) {
p[i++] = '0'+(s%10);
s/=10;
}
write(1, "sig ", 4);
for (i--; i>=0; i--) {
write(1, &p[i], 1);
}
write(1, "\n", 1);
}
跟踪输出:
execve("./accept", ["./accept"], [/* 57 vars */]) = 0
<skipped>
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
bind(3, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(3, 5) = 0
rt_sigaction(SIGQUIT, {0x4008c4, [QUIT], SA_RESTORER|SA_RESTART, 0x30b7e329a0}, {SIG_DFL, [], 0}, 8) = 0
accept(3, 0x7fffe3e3c500, [16]) = ? ERESTARTSYS (To be restarted)
--- SIGQUIT (Quit) @ 0 (0) ---
rt_sigaction(SIGQUIT, {0x4008c4, [QUIT], SA_RESTORER|SA_RESTART, 0x30b7e329a0}, {0x4008c4, [QUIT], SA_RESTORER|SA_RESTART, 0x30b7e329a0}, 8) = 0
write(1, "sig ", 4sig ) = 4
write(1, "3", 13) = 1
write(1, "\n", 1
) = 1
rt_sigreturn(0x1) = 43
accept(3, ^C <unfinished ...>
最佳答案
在Unix Network Programming内书,有一节说:
We used the term "slow system call" to describe
accept
, and we use this term for any system call that can block forever. That is, the system call need never return. Most networking functions fall into this category. For example, there is no guarantee that a server's call toaccept
will ever return, if there are no clients that will connect to the server. Similarly, our server's call toread
in Figure 5.3 will never return if the client never sends a line for the server to echo. Other examples of slow system calls are reads and writes of pipes and terminal devices. A notable exception is disk I/O, which usually returns to the caller (assuming no catastrophic hardware failure).The basic rule that applies here is that when a process is blocked in a slow system call and the process catches a signal and the signal handler returns, the system call can return an error of
EINTR
. Some kernels automatically restart some interrupted system calls. For portability, when we write a program that catches signals (most concurrent servers catchSIGCHLD
), we must be prepared for slow system calls to returnEINTR
. Portability problems are caused by the qualifiers "can" and "some," which were used earlier, and the fact that support for the POSIXSA_RESTART
flag is optional. Even if an implementation supports theSA_RESTART
flag, not all interrupted system calls may automatically be restarted. Most Berkeley-derived implementations, for example, never automatically restart select, and some of these implementations never restartaccept
orrecvfrom
.
关于linux - 为什么 Linux accept() 不返回 EINTR?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23685816/