linux系统中socket错误码:eintr和eagain的处理方法( 三 )


linux系统中socket错误码:eintr和eagain的处理方法

文章插图
 
#include poll.h int check_conn_is_ok(socket_t sock) { struct pollfd fd; int ret = 0; socklen_t len = 0;fd.fd = sock; fd.events = POLLOUT;while ( poll (&fd, 1, -1) == -1 ) {if( errno != EINTR ){perror("poll");return -1;} }len = sizeof(ret); if ( getsockopt (sock, SOL_SOCKET, SO_ERROR,&ret,&len) == -1 ) {perror("getsockopt");return -1; }if(ret != 0) {fprintf (stderr, "socket %d connect failed: %sn",sock, strerror (ret));return -1; }return 0;}在调用connect时 , 这样使用:
#include erron.h ....if(connnect()) {if(errno == EINTR) {if(check_conn_is_ok() < 0) {perror();return -1;}else {printf("connect is success!n");}}else {perror("connect");return -1;}}【linux系统中socket错误码:eintr和eagain的处理方法】我一般使用continue或者goto来处理 。
安装信号时设置 SA_RESTART属性
我们还可以从信号的角度来解决这个问题 ,  安装信号的时候 ,  设置 SA_RESTART属性 , 那么当信号处理函数返回后 ,  不会让系统调用返回失败 , 而是让被该信号中断的系统调用将自动恢复 。
struct sigaction action;action.sa_handler = handler_func;sigemptyset(&action.sa_mask);action.sa_flags = 0;/* 设置SA_RESTART属性 */action.sa_flags |= SA_RESTART;sigaction(SIGALRM, &action, NULL);但注意 , 并不是所有的系统调用都可以自动恢复 。如msgsnd喝msgrcv就是典型的例子 , msgsnd/msgrcv以block方式发送/接收消息时 , 会因为进程收到了信号而中断 。此时msgsnd/msgrcv将返回-1 , errno被设置为EINTR 。且即使在插入信号时设置了SA_RESTART , 也无效 。在man msgrcv中就有提到这点:
msgsnd and msgrcv are never automatically restarted after being interrupted by a signal handler, regardless of the setting of the SA_RESTART flag when establishing a signal handler.
 
忽略信号
当然最简单的方法是忽略信号 , 在安装信号时 , 明确告诉系统不会产生该信号的中断 。
struct sigaction action;action.sa_handler = SIG_IGN;sigemptyset(&action.sa_mask);sigaction(SIGALRM, &action, NULL);#include#include#include#include#include#includevoid sig_handler(int signum){printf("in handlern");sleep(1);printf("handler returnn");}int main(int argc, char **argv){char buf[100];int ret;struct sigaction action, old_action;action.sa_handler = sig_handler;sigemptyset(&action.sa_mask);action.sa_flags = 0;/* 版本1:不设置SA_RESTART属性* 版本2:设置SA_RESTART属性 *///action.sa_flags |= SA_RESTART;sigaction(SIGALRM, NULL, &old_action);if (old_action.sa_handler != SIG_IGN) {sigaction(SIGALRM, &action, NULL);}alarm(3);bzero(buf, 100);ret = read(0, buf, 100);if (ret == -1) {perror("read");}printf("read %d bytes:n", ret);printf("%sn", buf);return 0;}在linux测试结果:
不设置SA_RESTART , 执行结果如下:
linux系统中socket错误码:eintr和eagain的处理方法

文章插图
 
说明接受信号处理完成以后 , 主函数收到EINTR信号 , read函数返回-1 , 退出
设置SA_RESTART , 执行结果如下:
linux系统中socket错误码:eintr和eagain的处理方法

文章插图
 
说明设置SA_RESTART参数以后 , 自动重新调用read函数 , 没有体现在应用层代码中 , 在应用层看来 , 这个EINTR没有造成任何影响 。
总结:
慢系统调用(slow system call)会被信号中断 , 系统调用函数返回失败 , 并且errno被置为EINTR(错误描述为“Interrupted system call”) 。
处理方法有以下三种:①人为重启被中断的系统调用;②安装信号时设置 SA_RESTART属性;③忽略信号(让系统不产生信号中断) 。
有时我们需要捕获信号 , 但又考虑到第②种方法的局限性(设置 SA_RESTART属性对有的系统无效 , 如msgrcv) , 所以在编写代码时 , 一定要“人为重启被中断的系统调用” 。




推荐阅读