标签:网络编程 信号中断 慢系统调用 signal linux
这段时间在看Unix网络编程卷1,在5.9节处理SIGCHLD信号,关于处理僵死进程第四步如下写道:信号是在父进程阻塞于慢系统调用(accept)时由父进程捕获的,内核就会使慢系统调用(accept)返回一个EINTR错误。
看到上面那段落的时候,想到我前段时间写网络服务器遇到的问题,链接地址:http://bbs.csdn.net/topics/391032981,其实里面也有我关于这方面问题的困惑。
总结一下我论坛的那个问题,其实我无论如何是不能通过信号中断,测试epoll_wait出错errno置EINTR进而执行程序退出的,说个很简单的理由,当选用IDE进行调试程序的时候,若程序增加的有断点,程序会在步入断点的时候接收到SIGTRAP信号,这样一样会使慢系统调用返回错误并置errno为EINTR,这个时候的我肯定是不想程序就此退出。当然那个问题后来我是通过SocketPair解决的,其一用于epoll_wait监听,另一个在需要退出的时候写入数据,epoll_wait返回的时候,首先判断用于监听的那个套接字是否需要接收数据,若有数据需要接收那就是我通知程序退出呢。
接下来关于信号中断与慢系统调用的两个测试,测试环境Centos7 64,所有代码均无错误处理,请自行包含相关头文件
void sig_func(int signo) { printf("catch %d\n", signo); } int main(int argc, char *argv[]) { signal(SIGINT, sig_func); int lfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(1234); inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); bind(lfd, (struct sockaddr *)&addr, sizeof(addr)); listen(lfd, 10); while (true) { struct sockaddr_in caddr; socklen_t len = sizeof(caddr); int cfd = accept(lfd, (struct sockaddr *)&caddr, &len); printf("accept %d\n", cfd); } }
多次Ctrl+C,程序作如下输出:
[root@bogon Debug]# ./server ^Ccatch 2 ^Ccatch 2现像是通过Ctrl+C产生中断,并不会使accept函数返回,这和我们预想的不一样啊,这可能就是因为系统的signal已如Unix网络编程书中所说的那样,默认增加了自动重启这个标志,这时只能求助于手册,手册中有如此写道:
Furthermore, certain blocking system calls are automatically restarted if interrupted by a signal handler (see signal(7)). The BSD semantics are equivalent to calling sigaction(2) with the following flags: sa.sa_flags = SA_RESTART;
void sig_func(int signo) { printf("catch %d\n", signo); } int main(int argc, char *argv[]) { signal(SIGINT, sig_func); int lfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(1234); inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); bind(lfd, (struct sockaddr *)&addr, sizeof(addr)); listen(lfd, 10); fd_set fds; FD_ZERO(&fds); FD_SET(lfd, &fds); int res = select(lfd+1, &fds, NULL, NULL, NULL); printf("select %d -- errno %d\n", res, errno); }单次Ctrl+C,程序作如下输出:
[root@bogon Debug]# ./server ^Ccatch 2 select -1 -- errno 4
代码其实基本还是上面的代码,只是将想验证的系统调用换成了select调用,这个又和我们预想的一样,但是若真如我们手册中看到那样,这又是错误的啦,这时我们只能再次求助于帮助手册,又好好翻了翻手册,终于找到原因啦。
Interruption of system calls and library functions by signal handlers 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. Which of these two behaviors occurs depends on the interface and whether or not the signal handler was established using the SA_RESTART flag (see sigaction(2)). The details vary across UNIX systems; below, the details for Linux. 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). * open(2), if it can block (e.g., when opening a FIFO; see fifo(7)). * wait(2), wait3(2), wait4(2), waitid(2), and waitpid(2). * Socket interfaces: accept(2), connect(2), recv(2), recvfrom(2), recvmsg(2), send(2), sendto(2), and sendmsg(2), unless a timeout has been set on the socket (see below). * File locking interfaces: flock(2) and fcntl(2) F_SETLKW. * POSIX message queue interfaces: mq_receive(3), mq_timedreceive(3), mq_send(3), and mq_timedsend(3). * futex(2) FUTEX_WAIT (since Linux 2.6.22; beforehand, always failed with EINTR). * POSIX semaphore interfaces: sem_wait(3) and sem_timedwait(3) (since Linux 2.6.22; beforehand, always failed with EINTR). The following interfaces are never restarted after being interrupted by a signal handler, regardless of the use of SA_RESTART; they always fail with the error EINTR when interrupted by a signal handler: * Socket interfaces, when a timeout has been set on the socket using setsockopt(2): accept(2), recv(2), recvfrom(2), and recvmsg(2), if a receive timeout (SO_RCVTIMEO) has been set; connect(2), send(2), sendto(2), and sendmsg(2), if a send timeout (SO_SNDTIMEO) has been set. * Interfaces used to wait for signals: pause(2), sigsuspend(2), sigtimedwait(2), and sigwaitinfo(2). * File descriptor multiplexing interfaces: epoll_wait(2), epoll_pwait(2), poll(2), ppoll(2), select(2), and pselect(2). * System V IPC interfaces: msgrcv(2), msgsnd(2), semop(2), and semtimedop(2). * Sleep interfaces: clock_nanosleep(2), nanosleep(2), and usleep(3). * read(2) from an inotify(7) file descriptor. * io_getevents(2). The sleep(3) function is also never restarted if interrupted by a handler, but gives a success return: the number of seconds remaining to sleep.这里大致说明一下就是:当一个信号被捕获处理的时正阻塞着系统调用或是库函数,将会有两种情况发生,一种是调用将会自动重启当信号处理函数返回的时候,另一种是调用失败返回errno置EINTR。因为signal默认增加SA_RESTART标志,所以上面的是中断因为有标志自动重启的,下面的是无论如何都会返回错误的。
经过测试,若自己通过sigaction函数提供Signal函数调用,并在函数中取消SA_RESTART标志,这样的话,第一个测试程序将会有如下输出:
[root@bogon Debug]# ./server sigaction successed. ^Ccatch 2 accept -1 ^Ccatch 2 accept -1关于Signal函数这里不再实现。
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:网络编程 信号中断 慢系统调用 signal linux
原文地址:http://blog.csdn.net/panshiqu/article/details/47775719