注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

(转载)处理信号EINTR方法  

2015-02-04 10:38:18|  分类: 网络服务编程技术 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

(转载)处理信号EINTR方法

三篇BLOG

http://blog.chinaunix.net/uid-8996150-id-2011716.html

http://blog.csdn.net/bumanji/article/details/6302141

http://blog.chinaunix.net/uid-21501855-id-4490453.html

EINTR错误 2009-09-08 18:05:09

分类: LINUX

最近 socket 读数据老是遇到 Interrupted system call EINTR),代码改为如下解决

while (1)

{

    select(socket+1, &readfds, NULL, NULL, &tv);

    if (FD_ISSET(socket, &readfds))

      {

       printf("connection got!\n");

       break;

      }

    else{

       if (errno == EINTR)

          continue;

       else

          printf("Timed out.\n");

        }

}

 

下面的列表显示常见的 Linux 系统错误代码。

1 EPERM

Operation not permitted

操作不许可

2 ENOENT

No such file or directory

无此文件或目录

3 ESRCH

No such process

无此过程

4 EINTR

Interrupted system call

系统调用被禁止

5 EIO

I/O error

I/O 错误

6 ENXIO

No such device or address

无此器件或地址

7 E2BIG

Arg list too long

Arg 列表太长

8 ENOEXEC

Exec format error

Exec 格式错误

9 EBADF

Bad file number

文件数目错误

10 ECHILD

No child processes

无子过程

11 EAGAIN

Try again

再试一遍

12 ENOMEM

Out of memory

内存溢出

13 EACCES

Permission denied

许可拒绝

14 EFAULT

Bad address

错误的地址

15 ENOTBLK

Block device required

需要块设备

16 EBUSY

Device or resource busy

设备或资源忙

17 EEXIST

File exists

文件存在

18 EXDEV

Cross-device link

跨器链接

19 ENODEV

No such device

无此设备

20 ENOTDIR

Not a directory

不是一个目录

21 EISDIR

Is a directory

是一个目录

22 EINVAL

Invalid argument

无效的函数自变量

23 ENFILE

File table overflow

文件表溢出

24 EMFILE

Too many open files

打开的文件太多

25 ENOTTY

Inappropriate ioctl for device

 

26 ETXTBSY

Text file busy

文本文件忙

27 EFBIG

File too large

文件太大

28 ENOSPC

No space left on device

磁盘空间不足

29 ESPIPE

Illegal seek

不合法的寻找

30 EROFS

Read-only file system

只读文件系统

31 EMLINK

 

EINTR的处理

分类: Tips in action 2011-04-04 22:36 433人阅读 评论(0) 收藏 举报

postgresqlpathstruct

调用系统调用的时候,有时系统调用会被中断.此时,系统调用会返回-1,并且错误码被置为EINTR.但是,有时并不将这样的情况作为错误.有两种处理方法:

1.如果错误码为EINTR则重新调用系统调用,例如Postgresql中有一段代码:

[cpp] view plaincopy

retry1: 

    if (send(port->sock, &SSLok, 1, 0) != 1) 

    { 

        if (errno == EINTR) 

            goto retry1;    /* if interrupted, just retry */ 

2.重新定义系统调用,忽略错误码为EINTR的情况.例如,Cherokee中的一段代码:

[cpp] view plaincopy

int 

cherokee_stat (const char *restrict path, struct stat *buf) 

    int re; 

    do { 

        re = stat (path, buf); 

    } while ((re == -1) && (errno == EINTR)); 

    return re; 

 

socket中的函数遇见EINTR的处理 2014-09-22 16:54:23

分类: LINUX

这几天,写服务器代码过程当中,遇见EINRT信号的问题,我是借鉴 unp 》,采用continue或者goto again循环解决的。但是感觉这个还是很有必要记录一下。网络上查找到的信息很多。下面是我查找到的和EINTR有关的介绍:

1  http://blog.csdn.net/yanook/article/details/7226019  慢系统调用函数如何处理中断信号EINTR

2  http://blog.csdn.net/benkaoya/article/details/17262053 信号中断 慢系统调用

3  http://1.guotie.sinaapp.com/?p=235    socket,accept,connect出现EINTR错误的解决方法

个人认为2说的比较明确,建议大家多看看。

我的记录基本上是对2的抄袭:

 

慢系统调用:可能永远阻塞的系统调用,这很关键,不适用于非诸塞的情况。永远阻塞的系统调用是指调用永远无法返回,多数网络支持函数都属于这一类。如:若没有客户连接到服务器上,那么服务器的accept调用就会一直阻塞。

(以下为抄袭2原文)

EINTR说明:如果进程在一个慢系统调用(slow system call)中阻塞时,当捕获到某个信号且相应信号处理函数返回时,这个系统调用被中断,调用返回错误,设置errnoEINTR(相应的错误描述为“Interrupted system call”)。

怎么看哪些系统条用会产生EINTR错误呢?man 7 signal,在ubuntu 10.04上可以查看,哪些系统调用会产生 EINTR错误。

 

如何处理被中断的系统调用

既然系统调用会被中断,那么别忘了要处理被中断的系统调用。有三种处理方式:

人为重启被中断的系统调用

安装信号时设置 SA_RESTART属性(该方法对有的系统调用无效)

  忽略信号(让系统不产生信号中断)

人为重启被中断的系统调用

人为当碰到EINTR错误的时候,有一些可以重启的系统调用要进行重启,而对于有一些系统调用是不能够重启的。例如:acceptreadwriteselect、和open之类的函数来说,是可以进行重启的。不过对于套接字编程中的connect函数我们是不能重启的,若connect函数返回一个EINTR错误的时候,我们不能再次调用它,否则将立即返回一个错误。针对connect不能重启的处理方法是,必须调用select来等待连接完成。

这里的“重启”怎么理解?

一些IO系统调用执行时,如 read 等待输入期间,如果收到一个信号,系统将中断read 转而执行信号处理函数. 当信号处理返回后, 系统遇到了一个问题: 是重新开始这个系统调用, 还是让系统调用失败?早期UNIX系统的做法是, 中断系统调用,并让系统调用失败, 比如read返回 -1 同时设置 errno EINTR中断了的系统调用是没有完成的调用,它的失败是临时性的,如果再次调用则可能成功,这并不是真正的失败,所以要对这种情况进行处理, 典型的方式为:

 

另外,原文建议上去github上看看别人怎么处理EINTR错误的,下面2个处理方面均是截图过来的:

 

 

connect处理方式,抄袭3原文,没有测试过,处理方法是对的。

connect的问题,当connect遇到EINTR错误时,不能向上面那样重新进入循环处理,原因是,connect的请求已经发送向对方,正在等待对方回应,这是如果重新调用connect,而对方已经接受了上次的connect请求,这一次的connect就会被拒绝,因此,需要使用selectpoll调用来检查socket的状态,如果socket的状态就绪,则connect已经成功,否则,视错误原因,做对应的处理。

#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: %s\n",

                 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;

    }

}

我一般使用continue或者goto来处理。

安装信号时设置 SA_RESTART属性

我们还可以从信号的角度来解决这个问题,  安装信号的时候, 设置 SA_RESTART属性,那么当信号处理函数返回后, 不会让系统调用返回失败,而是让被该信号中断的系统调用将自动恢复。

[cpp] view plaincopyCODE上查看代码片派生到我的代码片

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); 

但注意,并不是所有的系统调用都可以自动恢复。如msgsndmsgrcv就是典型的例子,msgsnd/msgrcvblock方式发送/接收消息时,会因为进程收到了信号而中断。此时msgsnd/msgrcv将返回-1errno被设置为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.

忽略信号

当然最简单的方法是忽略信号,在安装信号时,明确告诉系统不会产生该信号的中断。

[cpp] view plaincopyCODE上查看代码片派生到我的代码片

struct sigaction action; 

  

action.sa_handler = SIG_IGN; 

sigemptyset(&action.sa_mask); 

  

sigaction(SIGALRM, &action, NULL); 

测试代码1

#include <signal.h> 

#include <stdio.h> 

#include <stdlib.h> 

#include <error.h> 

#include <string.h> 

#include <unistd.h> 

  

void sig_handler(int signum) 

    printf("in handler\n"); 

    sleep(1); 

    printf("handler return\n"); 

  

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("%s\n", buf); 

  

    return 0; 

ubuntu 10.04 上测试结果:

不设置SA_RESTART,执行结果如下:

(转载)处理信号EINTR方法 - 还东国 - 还东国的博客

 

说明接受信号处理完成以后,主函数收到EINTR信号,read函数返回-1,退出

设置SA_RESTART,执行结果如下:

(转载)处理信号EINTR方法 - 还东国 - 还东国的博客

 

说明设置SA_RESTART参数以后,自动重新调用read函数,没有体现在应用层代码中,在应用层看来,这个EINTR没有造成任何影响。

 

个人认为下面的总结很重要:

慢系统调用(slow system call)会被信号中断,系统调用函数返回失败,并且errno被置为EINTR(错误描述为“Interrupted system call”)。

处理方法有以下三种:①人为重启被中断的系统调用;②安装信号时设置 SA_RESTART属性;③忽略信号(让系统不产生信号中断)。

有时我们需要捕获信号,但又考虑到第②种方法的局限性(设置 SA_RESTART属性对有的系统无效,如msgrcv),所以在编写代码时,一定要“人为重启被中断的系统调用”。

  评论这张
 
阅读(540)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017